Hello community, here is the log from the commit of package yast2-users for openSUSE:Factory checked in at 2016-09-27 13:43:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/yast2-users (Old) and /work/SRC/openSUSE:Factory/.yast2-users.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "yast2-users" Changes: -------- --- /work/SRC/openSUSE:Factory/yast2-users/yast2-users.changes 2016-09-08 17:38:06.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.yast2-users.new/yast2-users.changes 2016-09-27 13:43:10.000000000 +0200 @@ -1,0 +2,21 @@ +Wed Sep 21 13:25:22 UTC 2016 - [email protected] + +- Do not require yast2-ldap for build time tests (bsc#999203). +- 3.1.60 + +------------------------------------------------------------------- +Tue Sep 20 15:10:23 UTC 2016 - [email protected] + +- Prevent a potential security issue if the target authorized_keys + file is not a regular file (related to FATE#319471) +- Fix authorized_keys section of AutoYaST schema +- 3.1.59 + +------------------------------------------------------------------- +Fri Sep 16 09:03:32 UTC 2016 - [email protected] + +- Add support to specify SSH authorized keys in the profile + (FATE#319471) +- 3.1.58 + +------------------------------------------------------------------- Old: ---- yast2-users-3.1.57.tar.bz2 New: ---- yast2-users-3.1.60.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ yast2-users.spec ++++++ --- /var/tmp/diff_new_pack.FBazxm/_old 2016-09-27 13:43:11.000000000 +0200 +++ /var/tmp/diff_new_pack.FBazxm/_new 2016-09-27 13:43:11.000000000 +0200 @@ -17,7 +17,7 @@ Name: yast2-users -Version: 3.1.57 +Version: 3.1.60 Release: 0 BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -33,7 +33,6 @@ BuildRequires: yast2 BuildRequires: yast2-core-devel BuildRequires: yast2-devtools >= 3.1.10 -BuildRequires: yast2-ldap >= 3.1.2 BuildRequires: yast2-perl-bindings BuildRequires: yast2-security BuildRequires: yast2-testsuite @@ -46,19 +45,7 @@ Requires: yast2-country Requires: yast2-pam Requires: yast2-security -Obsoletes: y2c_users -Obsoletes: y2t_inst-user -Obsoletes: y2t_users -Obsoletes: yast2-config-users -Obsoletes: yast2-trans-inst-user -Obsoletes: yast2-trans-users Obsoletes: yast2-users-devel-doc -Provides: y2c_users -Provides: y2t_inst-user -Provides: y2t_users -Provides: yast2-config-users -Provides: yast2-trans-inst-user -Provides: yast2-trans-users Conflicts: autoyast2 < 3.1.92 # older storage uses removed deprecated method, see https://github.com/yast/yast-storage/pull/187 Conflicts: yast2-storage < 3.1.75 @@ -90,6 +77,8 @@ %yast_build %install +# make testsuite/modules/Ldap.rb visible +export Y2BASE_Y2DIR=`pwd`/testsuite %yast_install %files @@ -103,6 +92,7 @@ %{yast_libdir}/users/clients/* %{yast_desktopdir}/*.desktop %{yast_moduledir}/*.pm +%{yast_moduledir}/SSHAuthorizedKeys.rb %{yast_moduledir}/UsersUI.rb %{yast_moduledir}/YaPI/*.pm %{yast_yncludedir}/users/* ++++++ yast2-users-3.1.57.tar.bz2 -> yast2-users-3.1.60.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/.travis.yml new/yast2-users-3.1.60/.travis.yml --- old/yast2-users-3.1.57/.travis.yml 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/.travis.yml 2016-09-21 16:21:01.000000000 +0200 @@ -5,12 +5,11 @@ # disable rvm, use system Ruby - rvm reset - wget https://raw.githubusercontent.com/yast/yast-devtools/master/travis-tools/travis_setup.sh - - sh ./travis_setup.sh -p "rake yast2-devtools yast2-testsuite yast2 yast2-perl-bindings yast2-core-dev yast2-ldap yast2-perl-bindings yast2-security libcrack2-dev doxygen libdigest-sha1-perl" -g "rspec:3.3.0 yast-rake gettext" + - sh ./travis_setup.sh -p "rake yast2-devtools yast2-testsuite yast2 yast2-perl-bindings yast2-core-dev yast2-perl-bindings yast2-security libcrack2-dev doxygen libdigest-sha1-perl" -g "rspec:3.3.0 yast-rake gettext" script: - rake check:syntax - rake check:pot - make -f Makefile.cvs - make - sudo make install - - make check - + - Y2BASE_Y2DIR=$(pwd)/testsuite make check diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/package/yast2-users.changes new/yast2-users-3.1.60/package/yast2-users.changes --- old/yast2-users-3.1.57/package/yast2-users.changes 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/package/yast2-users.changes 2016-09-21 16:21:01.000000000 +0200 @@ -1,4 +1,25 @@ ------------------------------------------------------------------- +Wed Sep 21 13:25:22 UTC 2016 - [email protected] + +- Do not require yast2-ldap for build time tests (bsc#999203). +- 3.1.60 + +------------------------------------------------------------------- +Tue Sep 20 15:10:23 UTC 2016 - [email protected] + +- Prevent a potential security issue if the target authorized_keys + file is not a regular file (related to FATE#319471) +- Fix authorized_keys section of AutoYaST schema +- 3.1.59 + +------------------------------------------------------------------- +Fri Sep 16 09:03:32 UTC 2016 - [email protected] + +- Add support to specify SSH authorized keys in the profile + (FATE#319471) +- 3.1.58 + +------------------------------------------------------------------- Fri Sep 2 15:16:37 CEST 2016 - [email protected] - AutoYaST: Ignore Users without UID while checking for duplicate diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/package/yast2-users.spec new/yast2-users-3.1.60/package/yast2-users.spec --- old/yast2-users-3.1.57/package/yast2-users.spec 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/package/yast2-users.spec 2016-09-21 16:21:01.000000000 +0200 @@ -17,7 +17,7 @@ Name: yast2-users -Version: 3.1.57 +Version: 3.1.60 Release: 0 BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -33,7 +33,6 @@ BuildRequires: yast2 BuildRequires: yast2-core-devel BuildRequires: yast2-devtools >= 3.1.10 -BuildRequires: yast2-ldap >= 3.1.2 BuildRequires: yast2-perl-bindings BuildRequires: yast2-security BuildRequires: yast2-testsuite @@ -46,19 +45,7 @@ Requires: yast2-country Requires: yast2-pam Requires: yast2-security -Obsoletes: y2c_users -Obsoletes: y2t_inst-user -Obsoletes: y2t_users -Obsoletes: yast2-config-users -Obsoletes: yast2-trans-inst-user -Obsoletes: yast2-trans-users Obsoletes: yast2-users-devel-doc -Provides: y2c_users -Provides: y2t_inst-user -Provides: y2t_users -Provides: yast2-config-users -Provides: yast2-trans-inst-user -Provides: yast2-trans-users Conflicts: autoyast2 < 3.1.92 # older storage uses removed deprecated method, see https://github.com/yast/yast-storage/pull/187 Conflicts: yast2-storage < 3.1.75 @@ -90,6 +77,8 @@ %yast_build %install +# make testsuite/modules/Ldap.rb visible +export Y2BASE_Y2DIR=`pwd`/testsuite %yast_install %files @@ -103,6 +92,7 @@ %{yast_libdir}/users/clients/* %{yast_desktopdir}/*.desktop %{yast_moduledir}/*.pm +%{yast_moduledir}/SSHAuthorizedKeys.rb %{yast_moduledir}/UsersUI.rb %{yast_moduledir}/YaPI/*.pm %{yast_yncludedir}/users/* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/src/Makefile.am new/yast2-users-3.1.60/src/Makefile.am --- old/yast2-users-3.1.57/src/Makefile.am 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/src/Makefile.am 2016-09-21 16:21:01.000000000 +0200 @@ -1,6 +1,7 @@ # Sources for users module_DATA = \ + modules/SSHAuthorizedKeys.rb \ modules/UsersUI.rb \ modules/UsersRoutines.pm \ modules/UsersPluginQuota.pm \ @@ -64,6 +65,8 @@ lib/users/encryption_method.rb \ lib/users/proposal.rb \ lib/users/encryption_proposal.rb \ + lib/users/ssh_authorized_keys_file.rb \ + lib/users/ssh_authorized_keyring.rb \ lib/users/users_database.rb scrconf_DATA = \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/src/autoyast-rnc/users.rnc new/yast2-users-3.1.60/src/autoyast-rnc/users.rnc --- old/yast2-users-3.1.57/src/autoyast-rnc/users.rnc 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/src/autoyast-rnc/users.rnc 2016-09-21 16:21:01.000000000 +0200 @@ -34,6 +34,17 @@ skel? & umask? } + +# by default, AutoYaST exports list entries as 'listentry' +authorized_key = + element authorized_key { text } | element listentry { text } + +authorized_keys = + element authorized_keys { + LIST, + authorized_key* +} + expire = element expire { text } ud_group = element group { text } ud_groups = element groups { text } @@ -55,7 +66,8 @@ username? & u_group? & u_groups? & - password_settings? + password_settings? & + authorized_keys? } encrypted = element encrypted { BOOLEAN } fullname = element fullname { text } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/src/lib/users/ssh_authorized_keyring.rb new/yast2-users-3.1.60/src/lib/users/ssh_authorized_keyring.rb --- old/yast2-users-3.1.57/src/lib/users/ssh_authorized_keyring.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-users-3.1.60/src/lib/users/ssh_authorized_keyring.rb 2016-09-21 16:21:01.000000000 +0200 @@ -0,0 +1,224 @@ +# Copyright (c) 2016 SUSE LLC. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 or 3 of the GNU General +# Public License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, contact SUSE LLC. +# +# To contact SUSE about this file by physical or electronic mail, +# you may find current contact information at www.suse.com + +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 each home directory in the + # system. + class SSHAuthorizedKeyring + include Logger + + # @return [Hash<String,Array<SSHAuthorizedKey>>] Authorized keys indexed by home directory + attr_reader :keys + private :keys + + # 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 clases should implement it. + # + # @return [String] Default message + def default_message + "Path error" + end + end + + # The home directory does not exist. + class HomeDoesNotExist < PathError + # @return default_message [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 default_message [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 default_message [String] Default error message + def default_message; "SSH directory is not a regular directory" end + end + + class NotRegularAuthorizedKeysFile < PathError + # @return default_message [String] Default error message + def default_message; "authorized_keys is not a regular file" end + end + + # Constructor + def initialize + @keys = {} + end + + # Add/register a keys + # + # This method does not make any change to the system. For that, + # see #write_keys. + # + # @param home [String] Home directory where the key will be stored + # @return [Array<SSHAuthorizedKey>] Registered authorized keys + def add_keys(home, new_keys) + keys[home] = new_keys + end + + # Returns the keys for a given home directory + # + # @return [Array<SSHAuthorizedKey>] List of authorized keys + def [](home) + keys[home] || [] + end + + # Determines if the keyring is empty + # + # @return [Boolean] +true+ if it's empty; +false+ otherwise + def empty? + keys.empty? + end + + # Read keys from a given home directory and add them to the keyring + # + # @param path [String] User's home directory + # @return [Boolean] +true+ if some key was found + def read_keys(home) + path = authorized_keys_path(home) + return false unless FileUtils::Exists(path) + authorized_keys = SSHAuthorizedKeysFile.new(path).keys + keys[home] = authorized_keys unless authorized_keys.empty? + log.info "Read #{authorized_keys.size} keys from #{path}" + !authorized_keys.empty? + 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. + # + # @param path [String] User's home directory + # @return [Boolean] +true+ if keys were written; +false+ otherwise + def write_keys(home) + return false if keys[home].nil? + if !FileUtils::Exists(home) + log.error("Home directory '#{home}' does not exist!") + raise HomeDoesNotExist.new(home) + end + user = FileUtils::GetOwnerUserID(home) + group = FileUtils::GetOwnerGroupID(home) + create_ssh_dir(home, user, group) + write_file(home, user, group) + end + + private + + # @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 + # @return [String] Permissions to be set on `authorized_keys` file + AUTHORIZED_KEYS_PERMS = "0600".freeze + + # Determine the path to the user's SSH directory + # + # @param home [String] Home directory + # @return [String] Path to the user's SSH directory + # + # @see SSH_DIR + def ssh_dir_path(home) + File.join(home, SSH_DIR) + end + + # Determine the path to the user's authorized keys file + # + # @param home [String] Home directory + # @return [String] Path to authorized keys file + # + # @see SSH_DIR + # @see AUTHORIZED_KEYS_FILE + # + # @see #ssh_dir_path + def authorized_keys_path(home) + File.join(ssh_dir_path(home), 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 home [String] Home directory where SSH directory must be created + # @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(home, user, group) + ssh_dir = ssh_dir_path(home) + if FileUtils::Exists(ssh_dir) + raise NotRegularSSHDirectory.new(ssh_dir) unless FileUtils::IsDirectory(ssh_dir) + return ssh_dir + end + ret = SCR.Execute(Path.new(".target.mkdir"), ssh_dir) + log.info("Creating SSH directory: #{ret}") + raise CouldNotCreateSSHDirectory.new(ssh_dir) unless ret + FileUtils::Chown("#{user}:#{group}", ssh_dir, false) && FileUtils::Chmod(SSH_DIR_PERMS, ssh_dir, false) + end + + # Write authorized keys file + # + # @param path [String] Path to file/directory + # @param user [Fixnum] Users's UID + # @param group [Fixnum] Group's GID + # @param perms [String] Permissions (in form "0700") + def write_file(home, owner, group) + path = authorized_keys_path(home) + file = SSHAuthorizedKeysFile.new(path) + file.keys = keys[home] + log.info "Writing #{keys[home].size} keys in #{path}" + file.save && FileUtils::Chown("#{owner}:#{group}", path, false) + rescue SSHAuthorizedKeysFile::NotRegularFile + raise NotRegularAuthorizedKeysFile.new(path) + end + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/src/lib/users/ssh_authorized_keys_file.rb new/yast2-users-3.1.60/src/lib/users/ssh_authorized_keys_file.rb --- old/yast2-users-3.1.57/src/lib/users/ssh_authorized_keys_file.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-users-3.1.60/src/lib/users/ssh_authorized_keys_file.rb 2016-09-21 16:21:01.000000000 +0200 @@ -0,0 +1,123 @@ +# Copyright (c) 2016 SUSE LLC. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 or 3 of the GNU General +# Public License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, contact SUSE LLC. +# +# To contact SUSE about this file by physical or electronic mail, +# you may find current contact information at www.suse.com + +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 + + # 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 + + # https://github.com/jordansissel/ruby-sshkeyauth/commit/12c9bb34399babf4040337e5695f3f453cd6745e#diff-4d8f3d488c1e25a30942c0e90f4e6ce4R14 + AUTHORIZED_KEYS_REGEX = + /\A((?:[A-Za-z0-9-]+(?:="[^"]+")?,?)+)? *((?:ssh|ecdsa)-[^ ]+) *([^ ]+) *(.+)?\z/ + + # 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 + if valid_key?(new_key) + self.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 + + # Determines is a string qualifies like a valid keys + # + # @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 FileUtils::Exists(path) + raise NotRegularFile unless FileUtils::IsFile(path) + else + SCR.Execute(Path.new(".target.bash"), "umask 0077 && touch #{path}") + end + content = keys.join("\n") + "\n" + SCR.Write(Path.new(".target.string"), path, content) + end + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/src/modules/SSHAuthorizedKeys.rb new/yast2-users-3.1.60/src/modules/SSHAuthorizedKeys.rb --- old/yast2-users-3.1.57/src/modules/SSHAuthorizedKeys.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-users-3.1.60/src/modules/SSHAuthorizedKeys.rb 2016-09-21 16:21:01.000000000 +0200 @@ -0,0 +1,113 @@ +# Copyright (c) 2016 SUSE LLC. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 or 3 of the GNU General +# Public License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, contact SUSE LLC. +# +# To contact SUSE about this file by physical or electronic mail, +# you may find current contact information at www.suse.com + +require "yast" +require "users/ssh_authorized_keyring" + +Yast.import "Message" + +module Yast + # This module add support to handle SSH authorized keys. + # + # It's inteded to be a thin layer on top of SSHAuthorizedKeyring to be used by + # yast2-users module (which is mainly written in Perl). + class SSHAuthorizedKeysClass < Module + include Logger + + attr_reader :keyring + + def main + reset + end + + # Read keys from a given home directory + # + # @see Yast::Users::SSHAuthorizedKeyring#read_keys + def read_keys(home) + keyring.read_keys(home) + end + + # Write keys to a given home directory + # + # @see Yast::Users::SSHAuthorizedKeyring#write_keys + def write_keys(home) + keyring.write_keys(home) + rescue Users::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 + log.warn(e.message) + Report.Warning( + # TRANSLATORS: '%s' is a directory path + format(_("'%s' exists but it is not a directory. It might be\n" \ + "a security issue so authorized keys will not\n" \ + "be written."), e.path) + ) + rescue Users::SSHAuthorizedKeyring::NotRegularAuthorizedKeysFile => e + log.warn(e.message) + Report.Warning( + # TRANSLATORS: '%s' is a directory path + format(_("'%s' exists but it is not a file. It might be\n" \ + "a security issue so authorized keys will not\n" \ + "be written."), e.path) + ) + rescue Users::SSHAuthorizedKeyring::CouldNotCreateSSHDirectory => e + log.warn(e.message) + Report.Warning( + Message.UnableToCreateDirectory(e.path) + "\n" + + _("Authorized keys will not be written.") + ) + end + + # Add a list of authorized keys for a given home directory + # + # @param path [String] User's home directory + # @param authorized_keys [Array<Hash|String>] + # @return [Boolean] +true+ if some key was imported; +false+ otherwise. + def import_keys(home, keys) + !keyring.add_keys(home, keys).empty? + end + + # Return a hash representation of the authorized keys + # + # To be used while exporting the AutoYaST profile. + # + # @param [String] Home directory where the authorized keys are located + # @return [Array<Hash>] Authorized keys for the given home + def export_keys(home) + keyring[home] + end + + # Initializes the module + def reset + @keyring = Users::SSHAuthorizedKeyring.new + end + + publish function: :import_keys, type: "boolean (string, list)" + publish function: :read_keys, type: "boolean (string)" + publish function: :write_keys, type: "boolean (string)" + publish function: :export_keys, type: "list<map> (string)" + end + + SSHAuthorizedKeys = SSHAuthorizedKeysClass.new + SSHAuthorizedKeys.main +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/src/modules/Users.pm new/yast2-users-3.1.60/src/modules/Users.pm --- old/yast2-users-3.1.57/src/modules/Users.pm 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/src/modules/Users.pm 2016-09-21 16:21:01.000000000 +0200 @@ -245,6 +245,7 @@ YaST::YCP::Import ("UsersRoutines"); YaST::YCP::Import ("UsersSimple"); YaST::YCP::Import ("UsersUI"); +YaST::YCP::Import ("SSHAuthorizedKeys"); ##------------------------------------------------------------------------- ##----------------- various routines -------------------------------------- @@ -4058,6 +4059,16 @@ } } +sub WriteAuthorizedKeys { + foreach my $username (keys %{$modified_users{"local"}}) { + my %user = %{$modified_users{"local"}{$username}}; + if ($user{"modified"} == "imported") { + # Write authorized keys to user's home (FATE#319471) + SSHAuthorizedKeys->write_keys($user{"homeDirectory"}); + } + } +} + ##------------------------------------ # execute USERDEL_PRECMD scripts for users which should be deleted sub PreDeleteUsers { @@ -4506,6 +4517,8 @@ $mode = $user{"home_mode"}; } UsersRoutines->ChmodHome($home, $mode); + # Write authorized keys to user's home (FATE#319471) + SSHAuthorizedKeys->write_keys($home); } } Syslog->Log ("User added by YaST: name=$username, uid=$uid, gid=$gid, home=$home"); @@ -4558,6 +4571,9 @@ } } } + + if (Mode->autoinst() || Mode->autoupgrade() || Mode->config()) { WriteAuthorizedKeys(); } + if (%users_with_crypted_dir) { unless (Package->Install ("cryptconfig")) { @@ -5959,6 +5975,11 @@ $ret{"shadowLastChange"} eq "") { $ret{"shadowLastChange"} = LastChangeIsNow (); } + + # Import authorized keys from profile (FATE#319471) + if ($user->{"authorized_keys"} && $ret{"homeDirectory"}) { + SSHAuthorizedKeys->import_keys($ret{"homeDirectory"}, $user->{"authorized_keys"}); + } return \%ret; } @@ -6463,6 +6484,13 @@ if (%user_shadow) { $ret{"password_settings"} = \%user_shadow; } + if ($user->{"homeDirectory"}) { + # Export authorized keys to profile (FATE#319471) + my $keys = SSHAuthorizedKeys->export_keys($user->{"homeDirectory"}); + if (@$keys) { + $ret{"authorized_keys"} = $keys; + } + } return \%ret; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/src/modules/UsersLDAP.pm new/yast2-users-3.1.60/src/modules/UsersLDAP.pm --- old/yast2-users-3.1.57/src/modules/UsersLDAP.pm 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/src/modules/UsersLDAP.pm 2016-09-21 16:21:01.000000000 +0200 @@ -1144,7 +1144,7 @@ if (!defined $data->{"uidNumber"}) { @internal = @group_internal_keys; } - foreach my $key (keys %{$data}) { + foreach my $key (sort keys %{$data}) { my $val = $data->{$key}; if (contains (\@internal, $key, 1)) { next; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/src/modules/UsersPasswd.pm new/yast2-users-3.1.60/src/modules/UsersPasswd.pm --- old/yast2-users-3.1.57/src/modules/UsersPasswd.pm 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/src/modules/UsersPasswd.pm 2016-09-21 16:21:01.000000000 +0200 @@ -33,6 +33,7 @@ YaST::YCP::Import ("FileUtils"); YaST::YCP::Import ("SCR"); +YaST::YCP::Import ("SSHAuthorizedKeys"); #--------------------------------------------------------------------- #--------------------------------------------------------- definitions @@ -299,6 +300,13 @@ return 1; } +# Read authorized keys from user's home (FATE#319471) +sub read_authorized_keys { + foreach my $user (values %{$users{"local"}}) { + SSHAuthorizedKeys->read_keys($user->{"homeDirectory"}); + } +} + # actually read /etc/passwd and save into internal structure sub read_passwd { @@ -518,6 +526,7 @@ } if (read_shadow () && read_group ()) { $ret = read_passwd (); + read_authorized_keys(); } $initialized = $ret; return $ret; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/test/Makefile.am new/yast2-users-3.1.60/test/Makefile.am --- old/yast2-users-3.1.57/test/Makefile.am 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/test/Makefile.am 2016-09-21 16:21:01.000000000 +0200 @@ -1,6 +1,10 @@ # Tests for users TESTS = \ - users_finish_test.rb + users_finish_test.rb \ + lib/users/ca_password_validator_test.rb \ + lib/users/encryption_method_test.rb \ + lib/users/ssh_authorized_keys_file_test.rb \ + lib/users/users_database_test.rb TEST_EXTENSIONS = .rb RB_LOG_COMPILER = rspec diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/test/fixtures/home/user1/.ssh/authorized_keys new/yast2-users-3.1.60/test/fixtures/home/user1/.ssh/authorized_keys --- old/yast2-users-3.1.57/test/fixtures/home/user1/.ssh/authorized_keys 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-users-3.1.60/test/fixtures/home/user1/.ssh/authorized_keys 2016-09-21 16:21:01.000000000 +0200 @@ -0,0 +1,2 @@ +environment="PATH=/usr/local/bin:$PATH",command="/srv/logon.sh $USER" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpZC8ctjmn90B/MxLOdSjYM3Yl3qd+BhTWYdBNgO3B1fJ1JSegTgCpDM0krMHqd/OAslW5H3MRED7g7g9WkKZh5xTMGvH56yRitJySfSiK8uSxCu6Jg7NM11kqOs5/RwycHO8955QrEYyiWOx80unD+CBJxGEZCOu/DH3ca4yEigAt2HSuC8NPicmRJWua6IbDa+VSICvdOTdFTM8izScSd5WBFH1ULz0bBfLnyi/pIiMjuHB69AN4gsUGYgKjzUsnufKli+DmzACgVWTdQ3Ukax/4/wgXFMr3KsDNpTbn7ZZOKzPpIXpzlP9AwbHQdym6J2NAPYV+DDY3Kcr/vql9 [email protected] +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCZXHfWaMch5VrgbogbW8lVuuwYCxQrgh00fF0V+GBBc0F6sux+WFlIENRLDNDSGBWol1X9LnbpgElzgM/PDX/3Uj+p+LVkt7sTk4k3tQQqFkrHEC+1TFnRk22AB4Xcw5KQ/bQnw1Cu0IfA/8c3c3Eh56WNiNi6F/bUeYKsdLLueGC/wKO/dCjM5xsLy/tXALrH0Y4NKbIZauM4BcEnZ7Cl6Wzl1AT/Mg+UK7bD8onufNd1l2w0rC0+BEy8VtBBobicp/Wv3nKkumKpNzP6jvpFE8CKiGx/fYzH/pLfe7bxEfBkKyR7A4gGWv6GHUaCYV+T+nac2ctWLLne1uQhRZcj [email protected] \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/test/fixtures/home/user2/.ssh/authorized_keys new/yast2-users-3.1.60/test/fixtures/home/user2/.ssh/authorized_keys --- old/yast2-users-3.1.57/test/fixtures/home/user2/.ssh/authorized_keys 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-users-3.1.60/test/fixtures/home/user2/.ssh/authorized_keys 2016-09-21 16:21:01.000000000 +0200 @@ -0,0 +1 @@ +# Prueba \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/test/fixtures/users_no_error.yml new/yast2-users-3.1.60/test/fixtures/users_no_error.yml --- old/yast2-users-3.1.57/test/fixtures/users_no_error.yml 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/test/fixtures/users_no_error.yml 2016-09-21 16:21:01.000000000 +0200 @@ -26,4 +26,9 @@ user_password: suse fullname: user_no_uid2 shell: /usr/bin/zsh - encrypted: true \ No newline at end of file + encrypted: true + authorized_keys: + - comment: [email protected] + keytype: ssh-rsa + content: abc123 + options: tunnel="0" \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/test/lib/users/ca_password_validator_test.rb new/yast2-users-3.1.60/test/lib/users/ca_password_validator_test.rb --- old/yast2-users-3.1.57/test/lib/users/ca_password_validator_test.rb 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/test/lib/users/ca_password_validator_test.rb 2016-09-21 16:21:01.000000000 +0200 @@ -1,4 +1,4 @@ -#! /usr/bin/rspec +#! /usr/bin/env rspec # Copyright (c) 2016 SUSE LLC. # All Rights Reserved. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/test/lib/users/encryption_method.rb new/yast2-users-3.1.60/test/lib/users/encryption_method.rb --- old/yast2-users-3.1.57/test/lib/users/encryption_method.rb 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/test/lib/users/encryption_method.rb 1970-01-01 01:00:00.000000000 +0100 @@ -1,60 +0,0 @@ -#! /usr/bin/rspec -# Copyright (c) 2016 SUSE LLC. -# All Rights Reserved. - -# This program is free software; you can redistribute it and/or -# modify it under the terms of version 2 or 3 of the GNU General -# Public License as published by the Free Software Foundation. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, contact SUSE LLC. - -# To contact SUSE about this file by physical or electronic mail, -# you may find current contact information at www.suse.com - -require_relative "../../test_helper" -require "users/encryption_method" -Yast.import "UsersSimple" - -describe Users::EncryptionMethod do - before do - allow(Yast::UsersSimple).to receive(:EncryptionMethod).and_return (current_method) - end - - subject { Users::EncryptionMethod } - - describe ".current" do - context "if an unknown method is returned by UsersSimple" do - let(:current_method) { "plain" } - - it "raises an exception" do - expect { subject.current }.to raise_error Users::EncryptionMethod::NotFoundError - end - end - - context "if a valid method is returned by UsersSimple" do - let(:current_method) { "sha256" } - - it "returns the corresponding object" do - expect(subject.current).to eq subject.new("sha256") - end - end - end - - describe "#current?" do - let(:current_method) { "des" } - - it "returns true if the method is the current one" do - expect(subject.new("des").current?).to eq true - end - - it "returns false if the method is not the current one" do - expect(subject.new("sha512").current?).to eq false - end - end -end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/test/lib/users/encryption_method_test.rb new/yast2-users-3.1.60/test/lib/users/encryption_method_test.rb --- old/yast2-users-3.1.57/test/lib/users/encryption_method_test.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-users-3.1.60/test/lib/users/encryption_method_test.rb 2016-09-21 16:21:01.000000000 +0200 @@ -0,0 +1,60 @@ +#! /usr/bin/env rspec +# Copyright (c) 2016 SUSE LLC. +# All Rights Reserved. + +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 or 3 of the GNU General +# Public License as published by the Free Software Foundation. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, contact SUSE LLC. + +# To contact SUSE about this file by physical or electronic mail, +# you may find current contact information at www.suse.com + +require_relative "../../test_helper" +require "users/encryption_method" +Yast.import "UsersSimple" + +describe Users::EncryptionMethod do + before do + allow(Yast::UsersSimple).to receive(:EncryptionMethod).and_return (current_method) + end + + subject { Users::EncryptionMethod } + + describe ".current" do + context "if an unknown method is returned by UsersSimple" do + let(:current_method) { "plain" } + + it "raises an exception" do + expect { subject.current }.to raise_error Users::EncryptionMethod::NotFoundError + end + end + + context "if a valid method is returned by UsersSimple" do + let(:current_method) { "sha256" } + + it "returns the corresponding object" do + expect(subject.current).to eq subject.new("sha256") + end + end + end + + describe "#current?" do + let(:current_method) { "des" } + + it "returns true if the method is the current one" do + expect(subject.new("des").current?).to eq true + end + + it "returns false if the method is not the current one" do + expect(subject.new("sha512").current?).to eq false + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/test/lib/users/local_password_test.rb new/yast2-users-3.1.60/test/lib/users/local_password_test.rb --- old/yast2-users-3.1.57/test/lib/users/local_password_test.rb 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/test/lib/users/local_password_test.rb 2016-09-21 16:21:01.000000000 +0200 @@ -1,4 +1,4 @@ -#! /usr/bin/rspec +#! /usr/bin/env rspec # Copyright (c) 2016 SUSE LLC. # All Rights Reserved. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/test/lib/users/ssh_authorized_keyring_test.rb new/yast2-users-3.1.60/test/lib/users/ssh_authorized_keyring_test.rb --- old/yast2-users-3.1.57/test/lib/users/ssh_authorized_keyring_test.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-users-3.1.60/test/lib/users/ssh_authorized_keyring_test.rb 2016-09-21 16:21:01.000000000 +0200 @@ -0,0 +1,210 @@ +#!/usr/bin/env rspec +# Copyright (c) 2016 SUSE LLC. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 or 3 of the GNU General +# Public License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, contact SUSE LLC. +# +# To contact SUSE about this file by physical or electronic mail, +# you may find current contact information at www.suse.com + +require_relative "../../test_helper" +require "users/ssh_authorized_keyring" +require "tmpdir" + +describe Yast::Users::SSHAuthorizedKeyring do + subject(:keyring) { Yast::Users::SSHAuthorizedKeyring.new } + + describe "#empty?" do + context "when keyring is empty" do + it "returns true" do + expect(keyring.empty?).to eq(true) + end + end + + context "when keyring is not empty" do + before { keyring.add_keys("/home/user", ["ssh-rsa 123ABC"]) } + + it "returns false" do + expect(keyring.empty?).to eq(false) + end + end + end + + describe "#read_keys" do + context "if some keys are present in the given home directory" do + let(:home) { FIXTURES_PATH.join("home", "user1").to_s } + + it "returns true" do + expect(keyring.read_keys(home)).to eq(true) + end + + it "registers defined keys" do + keyring.read_keys(home) + expect(keyring).to_not be_empty + end + end + + context "if no keys are present in the home directory" do + let(:home) { FIXTURES_PATH.join("home", "user2").to_s } + + it "returns false" do + expect(keyring.read_keys(home)).to eq(false) + end + + it "does not register any key" do + keyring.read_keys(home) + expect(keyring).to be_empty + end + end + + context "if authorized keys file does not exist" do + let(:home) { FIXTURES_PATH.join("home", "other").to_s } + + it "returns false" do + expect(keyring.read_keys(home)).to eq(false) + end + + it "does not register any key" do + keyring.read_keys(home) + expect(keyring).to be_empty + end + end + end + + describe "#write_keys" do + let(:tmpdir) { Dir.mktmpdir } + let(:home) { File.join(tmpdir, "/home/user") } + let(:file) { double("file", save: true) } + 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) } + after { FileUtils.rm_rf(tmpdir) if File.exist?(tmpdir) } + + context "if no keys are registered for the given home" do + it "returns false" do + expect(keyring.write_keys(home)).to eq(false) + end + + it "does not try to write the keys" do + expect(file).to_not receive(:save) + keyring.write_keys(home) + end + end + + context "if some keys are registered 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::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) + keyring.add_keys(home, [key]) + end + + it "writes the keys and returns true" do + expect(keyring.write_keys(home)).to eq(true) + expect(File).to exist(authorized_keys_path) + end + + 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) + + keyring.write_keys(home) + end + + it "sets SSH directory permissions to 0700" do + keyring.write_keys(home) + mode = File.stat(ssh_dir).mode.to_s(8) + expect(mode).to eq("40700") + end + + it "sets authorized_keys permissions to 0600" do + keyring.write_keys(home) + mode = File.stat(authorized_keys_path).mode.to_s(8) + expect(mode).to eq("100600") + end + + 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(home) } + .to raise_error(Yast::Users::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(Yast::SCR).to receive(:Execute) + .with(Yast::Path.new(".target.mkdir"), anything) + .and_return(false) + expect { keyring.write_keys(home) } + .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(home) } + .to raise_error(Yast::Users::SSHAuthorizedKeyring::NotRegularSSHDirectory) + end + end + + 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(home) + 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) + + expect { keyring.write_keys(home) } + .to raise_error(Yast::Users::SSHAuthorizedKeyring::NotRegularAuthorizedKeysFile) + end + end + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/test/lib/users/ssh_authorized_keys_file_test.rb new/yast2-users-3.1.60/test/lib/users/ssh_authorized_keys_file_test.rb --- old/yast2-users-3.1.57/test/lib/users/ssh_authorized_keys_file_test.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-users-3.1.60/test/lib/users/ssh_authorized_keys_file_test.rb 2016-09-21 16:21:01.000000000 +0200 @@ -0,0 +1,149 @@ +#!/usr/bin/env rspec +# Copyright (c) 2016 SUSE LLC. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 or 3 of the GNU General +# Public License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, contact SUSE LLC. +# +# To contact SUSE about this file by physical or electronic mail, +# you may find current contact information at www.suse.com +require_relative "../../test_helper" +require "users/ssh_authorized_keys_file" + +describe Yast::Users::SSHAuthorizedKeysFile do + subject(:file) { Yast::Users::SSHAuthorizedKeysFile.new(path) } + let(:path) { FIXTURES_PATH.join("home", "user1", ".ssh", "authorized_keys") } + + describe "#keys" do + context "when file is empty" do + let(:path) { FIXTURES_PATH.join("home", "user2", ".ssh", "authorized_keys") } + + it "returns an empty array" do + expect(subject.keys).to eq([]) + end + end + + context "when file contains some keys" do + let(:path) { FIXTURES_PATH.join("home", "user1", ".ssh", "authorized_keys") } + + it "returns the keys that are present in the file" do + first, second = subject.keys + expect(first).to match(/environment=.+/) + expect(second).to match(/ssh-rsa/) + end + end + + context "when file does not exist" do + let(:path) { FIXTURES_PATH.join("non-existent-file") } + + it "returns an empty array" do + expect(subject.keys).to eq([]) + end + end + end + + describe "#keys=" do + let(:key) { "ssh-dsa 123ABC" } + + it "sets file keys" do + file.keys = [key] + expect(file.keys).to eq([key]) + end + end + + describe "#save" do + let(:key0) { "ssh-dsa 123ABC" } + let(:key1) { "ssh-rsa 456DEF" } + let(:expected_content) { "ssh-dsa 123ABC\nssh-rsa 456DEF\n" } + + let(:path) { "/tmp/home/user" } + 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 + + context "if the directory exists" do + let(:dir_exists) { true } + + 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 + + context "and the file exists and it is a regular one" do + let(:file_exists) { true } + + it "updates the file with the registered keys and returns true" do + allow(Yast::FileUtils).to receive(:IsFile).with(path) + .and_return(true) + + expect(Yast::SCR).to receive(:Write) + .with(Yast::Path.new(".target.string"), path, expected_content) + file.keys = [key0, key1] + file.save + end + end + + context "and the file exists but it is not a regular one" do + let(:file_exists) { true } + + it "raises NotRegularFile exception and does not update the file" do + allow(Yast::FileUtils).to receive(:IsFile).with(path) + .and_return(false) + + 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) + end + end + 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) + end + end + 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") } + + 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") } + + it "adds the new key" do + file.add_key(key) + expect(file.keys).to eq([key]) + end + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/test/lib/users/users_database_test.rb new/yast2-users-3.1.60/test/lib/users/users_database_test.rb --- old/yast2-users-3.1.57/test/lib/users/users_database_test.rb 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/test/lib/users/users_database_test.rb 2016-09-21 16:21:01.000000000 +0200 @@ -1,4 +1,4 @@ -#! /usr/bin/rspec +#! /usr/bin/env rspec # Copyright (c) 2016 SUSE LLC. # All Rights Reserved. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/test/ssh_authorized_keys_test.rb new/yast2-users-3.1.60/test/ssh_authorized_keys_test.rb --- old/yast2-users-3.1.57/test/ssh_authorized_keys_test.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-users-3.1.60/test/ssh_authorized_keys_test.rb 2016-09-21 16:21:01.000000000 +0200 @@ -0,0 +1,129 @@ +#!/usr/bin/env rspec +# Copyright (c) 2016 SUSE LLC. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 or 3 of the GNU General +# Public License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, contact SUSE LLC. +# +# To contact SUSE about this file by physical or electronic mail, +# you may find current contact information at www.suse.com + +require_relative "test_helper" +require "users/ssh_authorized_keyring" +require "tmpdir" + +Yast.import "SSHAuthorizedKeys" +Yast.import "Report" + +describe Yast::SSHAuthorizedKeys do + subject(:ssh_authorized_keys) { Yast::SSHAuthorizedKeys } + + let(:valid_key_spec) { double("valid_key") } + let(:invalid_key_spec) { double("invalid_key") } + let(:home) { "/home/user" } + let(:ssh_dir) { File.join(home, ".ssh") } + let(:authorized_keys_path) { File.join("ssh_dir", ".authorized_keys") } + let(:key) { double("key") } + let(:keys) { [key] } + + before { subject.reset } + + describe "#import_keys" do + it "imports keys into the keyring" do + expect(subject.keyring).to receive(:add_keys).with(home, keys) + .and_return(keys) + subject.import_keys(home, keys) + end + + it "returns true if some key was imported" do + allow(subject.keyring).to receive(:add_keys).with(home, keys) + .and_return(keys) + expect(subject.import_keys(home, keys)).to eq(true) + end + + it "returns false if no key was imported" do + allow(subject.keyring).to receive(:add_keys).with(home, keys) + .and_return([]) + expect(subject.import_keys(home, keys)).to eq(false) + end + end + + describe "#export_keys" do + context "when some key was added" do + before { subject.import_keys(home, keys) } + + it "returns an array with added keys" do + expect(subject.export_keys(home)).to eq(keys) + end + end + + context "when no key was added" do + it "returns an empty array" do + expect(subject.export_keys(home)).to eq([]) + end + end + end + + describe "#write_keys" do + context "when home directory does not exists" do + let(:exception) do + Yast::Users::SSHAuthorizedKeyring::HomeDoesNotExist.new(home) + end + + it "shows an error message" do + allow(subject.keyring).to receive(:write_keys).and_raise(exception) + expect(Yast::Report).to receive(:Warning) + .with(/'#{home}' does not exist/) + subject.write_keys(home) + end + end + + context "SSH directory is not a directory" do + let(:exception) do + Yast::Users::SSHAuthorizedKeyring::NotRegularSSHDirectory.new(ssh_dir) + end + + it "shows an error message" do + allow(subject.keyring).to receive(:write_keys).and_raise(exception) + expect(Yast::Report).to receive(:Warning) + .with(/'#{ssh_dir}' exists but it is not a directory/) + subject.write_keys(home) + end + end + + context "SSH directory could not be created" do + let(:exception) do + Yast::Users::SSHAuthorizedKeyring::CouldNotCreateSSHDirectory.new(ssh_dir) + end + + it "shows an error message" do + allow(subject.keyring).to receive(:write_keys).and_raise(exception) + expect(Yast::Report).to receive(:Warning) + .with(/not create directory '#{ssh_dir}'/) + subject.write_keys(home) + end + end + + context "authorized_keys exists but it's not a regular file" do + let(:exception) do + Yast::Users::SSHAuthorizedKeyring::NotRegularAuthorizedKeysFile.new(authorized_keys_path) + end + + it "shows an error message" do + allow(subject.keyring).to receive(:write_keys).and_raise(exception) + expect(Yast::Report).to receive(:Warning) + .with(/'#{authorized_keys_path}' exists but it is not a file/) + subject.write_keys(home) + end + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/test/test_helper.rb new/yast2-users-3.1.60/test/test_helper.rb --- old/yast2-users-3.1.57/test/test_helper.rb 2016-09-02 16:36:17.000000000 +0200 +++ new/yast2-users-3.1.60/test/test_helper.rb 2016-09-21 16:21:01.000000000 +0200 @@ -20,6 +20,10 @@ src_path = File.expand_path("../../src", __FILE__) ENV["Y2DIR"] = src_path +# make sure we run the tests in English locale +# (some tests check the output which is marked for translation) +ENV["LC_ALL"] = "en_US.UTF-8" + require "yast" require "pathname" require "yast/rspec" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/testsuite/modules/Ldap.rb new/yast2-users-3.1.60/testsuite/modules/Ldap.rb --- old/yast2-users-3.1.57/testsuite/modules/Ldap.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-users-3.1.60/testsuite/modules/Ldap.rb 2016-09-21 16:21:01.000000000 +0200 @@ -0,0 +1,9 @@ +require "yast" +module Yast + # This exists because it is hard to use RSpec mocks for code + # that goes through Perl (like UsersLdap.pm) + class LdapClass < Module + Builtins.y2milestone("Using a mock Ldap module") + end + Ldap = LdapClass.new +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/testsuite/tests/ConvertMap.out new/yast2-users-3.1.60/testsuite/tests/ConvertMap.out --- old/yast2-users-3.1.57/testsuite/tests/ConvertMap.out 2016-09-02 16:36:18.000000000 +0200 +++ new/yast2-users-3.1.60/testsuite/tests/ConvertMap.out 2016-09-21 16:21:01.000000000 +0200 @@ -1,24 +1,31 @@ Dump ========================================================== Dump --------- user (loginshell is changed): Dump $["dn":"uid=test2,ou=people,dc=suse,dc=cz", "encrypted":true, "gecos":"test", "gidNumber":100, "grouplist":$["audio":1], "groupname":"users", "loginShell":"/bin/csh", "modified":"edited", "objectClass":["inetOrgPerson", "posixAccount", "shadowAccount", "top"], "org_uid":"test2", "org_uidnumber":11111, "org_user":$["dn":"uid=test2,ou=people,dc=suse,dc=cz", "gecos":"test", "gidNumber":100, "grouplist":$["audio":1], "groupname":"users", "loginShell":"/bin/bash", "objectClass":["inetOrgPerson", "posixAccount", "shadowAccount", "top"], "type":"ldap", "uid":"test2", "uidNumber":11111, "userPassword":"{crypt}IVj2W92x/IuFs"], "plugins":["UsersPluginLDAPAll"], "type":"ldap", "uid":"test2", "uidNumber":11111, "userPassword":"{crypt}IVj2W92x/IuFs", "what":"edit_user"] +Read .ldap.schema.at $["name":"loginShell"] $["usage":1] Return $["loginShell":"/bin/csh"] Dump --------- converted : Dump $["loginShell":"/bin/csh"] Dump ========================================================== Dump --------- user (new password): Dump $["dn":"uid=test2,ou=people,dc=suse,dc=cz", "encrypted":true, "gecos":"test", "gidNumber":100, "grouplist":$["audio":1], "groupname":"users", "loginShell":"/bin/csh", "modified":"edited", "objectClass":["inetOrgPerson", "posixAccount", "shadowAccount", "top"], "org_uid":"test2", "org_uidnumber":11111, "org_user":$["dn":"uid=test2,ou=people,dc=suse,dc=cz", "gecos":"test", "gidNumber":100, "grouplist":$["audio":1], "groupname":"users", "loginShell":"/bin/bash", "objectClass":["inetOrgPerson", "posixAccount", "shadowAccount", "top"], "type":"ldap", "uid":"test2", "uidNumber":11111, "userPassword":"{crypt}IVj2W92x/IuFs"], "plugins":["UsersPluginLDAPAll"], "type":"ldap", "uid":"test2", "uidNumber":11111, "userPassword":"qqqqq", "what":"edit_user"] +Read .ldap.schema.at $["name":"userPassword"] $["usage":1] Return $["loginShell":"/bin/csh", "userPassword":"{crypt}qqqqq"] Dump --------- converted : Dump $["loginShell":"/bin/csh", "userPassword":"{crypt}qqqqq"] Dump ========================================================== Dump --------- user (new object class): Dump $["dn":"uid=test2,ou=people,dc=suse,dc=cz", "encrypted":true, "gecos":"test", "gidNumber":100, "grouplist":$["audio":1], "groupname":"users", "loginShell":"/bin/csh", "modified":"edited", "objectClass":["inetOrgPerson", "posixAccount", "shadowAccount", "top", "sambaSamAccount"], "org_uid":"test2", "org_uidnumber":11111, "org_user":$["dn":"uid=test2,ou=people,dc=suse,dc=cz", "gecos":"test", "gidNumber":100, "grouplist":$["audio":1], "groupname":"users", "loginShell":"/bin/bash", "objectClass":["inetOrgPerson", "posixAccount", "shadowAccount", "top"], "type":"ldap", "uid":"test2", "uidNumber":11111, "userPassword":"{crypt}qqqqq"], "plugins":["UsersPluginLDAPAll"], "type":"ldap", "uid":"test2", "uidNumber":11111, "userPassword":"{crypt}qqqqq", "what":"edit_user"] +Read .ldap.schema.at $["name":"objectClass"] $["usage":1] Return $["loginShell":"/bin/csh", "objectClass":["inetOrgPerson", "posixAccount", "shadowAccount", "top", "sambaSamAccount"]] Dump --------- converted : Dump $["loginShell":"/bin/csh", "objectClass":["inetOrgPerson", "posixAccount", "shadowAccount", "top", "sambaSamAccount"]] Dump ========================================================== Dump --------- user (removed 'org_user' submap): Dump $["dn":"uid=test2,ou=people,dc=suse,dc=cz", "encrypted":true, "gecos":"test", "gidNumber":100, "grouplist":$["audio":1], "groupname":"users", "loginShell":"/bin/csh", "modified":"edited", "objectClass":["inetOrgPerson", "posixAccount", "shadowAccount", "top", "sambaSamAccount"], "org_uid":"test2", "org_uidnumber":11111, "plugins":["UsersPluginLDAPAll"], "type":"ldap", "uid":"test2", "uidNumber":11111, "userPassword":"{crypt}qqqqq", "what":"edit_user"] +Read .ldap.schema.at $["name":"gecos"] $["usage":1] +Read .ldap.schema.at $["name":"gidNumber"] $["usage":1] +Read .ldap.schema.at $["name":"uid"] $["usage":1] +Read .ldap.schema.at $["name":"uidNumber"] $["usage":1] Return $["gecos":"test", "gidNumber":"100", "loginShell":"/bin/csh", "objectClass":["inetOrgPerson", "posixAccount", "shadowAccount", "top", "sambaSamAccount"], "uid":"test2", "uidNumber":"11111", "userPassword":"{crypt}qqqqq"] Dump --------- converted : Dump $["gecos":"test", "gidNumber":"100", "loginShell":"/bin/csh", "objectClass":["inetOrgPerson", "posixAccount", "shadowAccount", "top", "sambaSamAccount"], "uid":"test2", "uidNumber":"11111", "userPassword":"{crypt}qqqqq"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-3.1.57/testsuite/tests/ConvertMap.rb new/yast2-users-3.1.60/testsuite/tests/ConvertMap.rb --- old/yast2-users-3.1.57/testsuite/tests/ConvertMap.rb 2016-09-02 16:36:18.000000000 +0200 +++ new/yast2-users-3.1.60/testsuite/tests/ConvertMap.rb 2016-09-21 16:21:01.000000000 +0200 @@ -21,6 +21,7 @@ }, "ldap" => { "schema" => { + "at" => { "usage" => 1 }, "oc" => { "may" => [ "gidNumber",
