Skip to content

Commit

Permalink
Merge pull request #350 from yast/y2users-missing-attrs
Browse files Browse the repository at this point in the history
Read home permissions and root aliases
  • Loading branch information
dgdavid committed Nov 4, 2021
2 parents 7d1b1a5 + 6787d51 commit 8095bbf
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 99 deletions.
42 changes: 0 additions & 42 deletions src/lib/y2users/linux/base_reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,9 @@
require "yast"
require "abstract_method"
require "y2users/config"
require "y2users/login_config"
require "y2users/parsers/group"
require "y2users/parsers/passwd"
require "y2users/parsers/shadow"
require "y2users/linux/useradd_config_reader"
require "users/ssh_authorized_keyring"

Yast.import "Autologin"

module Y2Users
module Linux
Expand All @@ -42,9 +37,6 @@ def read
Config.new.tap do |config|
read_elements(config)
read_passwords(config)
read_authorized_keys(config)
read_useradd_config(config)
read_login(config)
end
end

Expand Down Expand Up @@ -107,40 +99,6 @@ def read_passwords(config)
# @!method load_passwords
# @return [String] loaded passwords from the system
abstract_method :load_passwords

# Reads users authorized keys
#
# @see Yast::Users::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
end
end

# Reads the configuration for useradd
#
# @param config [Config]
def read_useradd_config(config)
config.useradd = UseraddConfigReader.new.read
end

# Reads the login information
#
# @param config [Config]
def read_login(config)
Yast::Autologin.Read

return unless Yast::Autologin.used

login = LoginConfig.new
login.autologin_user = config.users.by_name(Yast::Autologin.user)
login.passwordless = Yast::Autologin.pw_less

config.login = login
end
end
end
end
90 changes: 89 additions & 1 deletion src/lib/y2users/linux/reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,100 @@

require "yast2/execute"
require "y2users/linux/base_reader"
require "y2users/login_config"
require "y2users/linux/useradd_config_reader"
require "users/ssh_authorized_keyring"

Yast.import "Autologin"
Yast.import "MailAliases"

module Y2Users
module Linux
# Reads users configuration from the system using `getent` command.
class Reader < BaseReader
private # rubocop:disable Layout/IndentationWidth
# @see BaseReader#read
# @note some of these #read_* methods could be moved to the
# {BaseReader#read} once they allow reading from a specific location
# (i.e., compatible with {LocalReader} too)
def read
config = super

read_root_aliases(config)
read_home_permissions(config)
read_authorized_keys(config)
read_useradd_config(config)
read_login(config)

config
end

private

# Set root aliases (i.e., users receiving system mails)
#
# @see Yast::MailAliases#GetRootAlias
#
# @param config [Config]
def read_root_aliases(config)
Yast::MailAliases.GetRootAlias.split(", ").each do |name|
user = config.users.by_name(name)
user.receive_system_mail = true if user
end
end

# Command for reading home directory permissions
STAT = "/usr/bin/stat".freeze
private_constant :STAT

# Reads home permissions
#
# @return [Array<Y2Users::User>]
def read_home_permissions(config)
config.users.reject(&:system?).each do |user|
next unless user.home && Dir.exist?(user.home.path)

# "stat --printf %#a" returns the permissions in octal format
# (e.g., 0755), as expected by {Home#permissions}.
user.home.permissions = Yast::Execute.on_target!(
STAT, "--printf", "%#a", user.home.path,
stdout: :capture
)
end
end

# Reads users authorized keys
#
# @see Yast::Users::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
end
end

# Reads the configuration for useradd
#
# @param config [Config]
def read_useradd_config(config)
config.useradd = UseraddConfigReader.new.read
end

# Reads the login information
#
# @param config [Config]
def read_login(config)
Yast::Autologin.Read

return unless Yast::Autologin.used

login = LoginConfig.new
login.autologin_user = config.users.by_name(Yast::Autologin.user)
login.passwordless = Yast::Autologin.pw_less

config.login = login
end

# Loads entries from `passwd` database
#
Expand Down
10 changes: 2 additions & 8 deletions test/lib/y2users/linux/base_reader_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@
require "y2users/linux/base_reader"

describe Y2Users::Linux::BaseReader do
around do |example|
# Let's use test/fixtures/home as src root for reading authorized keys from there
change_scr_root(FIXTURES_PATH.join("home")) { example.run }
end

describe "#read" do
let(:passwd_content) { File.read(File.join(FIXTURES_PATH, "/root/etc/passwd")) }
let(:group_content) { File.read(File.join(FIXTURES_PATH, "/root/etc/group")) }
Expand All @@ -38,12 +33,12 @@
let(:expected_root_auth_keys) { authorized_keys_from(root_home) }

before do
allow(subject.log).to receive(:warn)

# mock Yast::Execute calls and provide file content from fixture
allow(subject).to receive(:load_users).and_return(passwd_content)
allow(subject).to receive(:load_groups).and_return(group_content)
allow(subject).to receive(:load_passwords).and_return(shadow_content)

allow(subject.log).to receive(:warn)
end

it "generates a config with read data" do
Expand All @@ -61,7 +56,6 @@
expect(root_user.primary_group.name).to eq "root"
expect(root_user.password.value.encrypted?).to eq true
expect(root_user.password.value.content).to match(/^\$6\$pL/)
expect(root_user.authorized_keys).to eq(expected_root_auth_keys)
end

it "logs warning if password found for not existing user" do
Expand Down
20 changes: 0 additions & 20 deletions test/lib/y2users/linux/local_reader_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,10 @@
before do
allow(Yast::Execute).to receive(:on_target!).with(/useradd/, "-D", anything)
.and_return(useradd_default_values)

allow(Yast::ShadowConfig).to receive(:fetch).with(:umask).and_return("044")
end

describe "#read" do
context "when all expected files are present" do
let(:root_home) { FIXTURES_PATH.join("home", "root").to_s }
let(:expected_root_auth_keys) { authorized_keys_from(root_home) }

it "generates a config with read data" do
config = subject.read

Expand All @@ -60,14 +55,6 @@
expect(root_user.primary_group.name).to eq "root"
expect(root_user.password.value.encrypted?).to eq true
expect(root_user.password.value.content).to match(/^\$6\$pL/)
expect(root_user.authorized_keys).to eq(expected_root_auth_keys)

useradd = config.useradd
expect(useradd.group).to eq "100"
expect(useradd.expiration).to eq ""
expect(useradd.inactivity_period).to eq(-1)
expect(useradd.create_mail_spool).to eq true
expect(useradd.umask).to eq "044"

expect(config.login?).to eq(false)
end
Expand Down Expand Up @@ -96,13 +83,6 @@
expect(config.users.size).to eq 0
expect(config.groups.size).to eq 0

useradd = config.useradd
expect(useradd.group).to eq "100"
expect(useradd.expiration).to eq ""
expect(useradd.inactivity_period).to eq(-1)
expect(useradd.create_mail_spool).to eq true
expect(useradd.umask).to eq "044"

expect(config.login?).to eq(false)
end
end
Expand Down
80 changes: 52 additions & 28 deletions test/lib/y2users/linux/reader_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,42 +25,50 @@
require "y2users/linux/reader"

describe Y2Users::Linux::Reader do
around do |example|
# Let's use test/fixtures/home as src root for reading authorized keys from there
change_scr_root(FIXTURES_PATH.join("home")) { example.run }
end

before do
# mock Yast::Execute calls and provide file content from fixture
passwd_content = File.read(File.join(FIXTURES_PATH, "/root/etc/passwd"))
allow(Yast::Execute).to receive(:on_target!).with(/getent/, "passwd", anything)
.and_return(passwd_content)

group_content = File.read(File.join(FIXTURES_PATH, "/root/etc/group"))
allow(Yast::Execute).to receive(:on_target!).with(/getent/, "group", anything)
.and_return(group_content)

shadow_content = File.read(File.join(FIXTURES_PATH, "/root/etc/shadow"))
allow(Yast::Execute).to receive(:on_target!).with(/getent/, "shadow", anything)
.and_return(shadow_content)

allow(Yast::Execute).to receive(:on_target!).with(/useradd/, "-D", anything)
.and_return(useradd_default_values)

allow(Yast::ShadowConfig).to receive(:fetch).with(:umask).and_return("024")
end

describe "#read" do
let(:passwd_content) { File.read(File.join(FIXTURES_PATH, "/root2/etc/passwd")) }
let(:group_content) { File.read(File.join(FIXTURES_PATH, "/root2/etc/group")) }
let(:shadow_content) { File.read(File.join(FIXTURES_PATH, "/root2/etc/shadow")) }
let(:root_home) { FIXTURES_PATH.join("home", "root").to_s }
let(:expected_root_auth_keys) { authorized_keys_from(root_home) }

around do |example|
# Let's use test/fixtures/home as src root for reading authorized keys from there
change_scr_root(FIXTURES_PATH.join("home")) { example.run }
end

before do
# mock Yast::Execute calls and provide file content from fixture
allow(Yast::Execute).to receive(:on_target!).with(/getent/, "passwd", anything)
.and_return(passwd_content)
allow(Yast::Execute).to receive(:on_target!).with(/getent/, "group", anything)
.and_return(group_content)
allow(Yast::Execute).to receive(:on_target!).with(/getent/, "shadow", anything)
.and_return(shadow_content)
allow(Yast::Execute).to receive(:on_target!).with(/useradd/, "-D", anything)
.and_return(useradd_default_values)

allow(Yast::ShadowConfig).to receive(:fetch)
allow(Yast::ShadowConfig).to receive(:fetch).with(:umask).and_return("044")

# mocks root aliases
allow(Yast::MailAliases).to receive(:GetRootAlias).and_return("games, unknown, news")

# mocks to check reading of home permissions
allow(Dir).to receive(:exist?)
allow(Dir).to receive(:exist?).with("/home/a_user").and_return(true)
allow(Yast::Execute).to receive(:on_target!)
.with("/usr/bin/stat", any_args, "/home/a_user", stdout: :capture)
.and_return("0700")
end

it "generates a config with read data" do
config = subject.read

expect(config).to be_a(Y2Users::Config)

expect(config.users.size).to eq 18
expect(config.groups.size).to eq 37
expect(config.users.size).to eq 19
expect(config.groups.size).to eq 7

root_user = config.users.root
expect(root_user.uid).to eq "0"
Expand All @@ -78,11 +86,27 @@
expect(useradd.expiration).to eq ""
expect(useradd.inactivity_period).to eq(-1)
expect(useradd.create_mail_spool).to eq true
expect(useradd.umask).to eq "024"
expect(useradd.umask).to eq "044"

expect(config.login?).to eq(false)
end

it "sets root aliases" do
config = subject.read

root_aliases = config.users.select(&:receive_system_mail?)

expect(root_aliases.size).to eq 2
expect(root_aliases.map(&:name)).to contain_exactly("games", "news")
end

it "sets home permissions (octal number starting by 0)" do
config = subject.read

user = config.users.by_name("a_user")
expect(user.home.permissions).to eq("0700")
end

context "when there are login settings" do
before do
allow(Yast::Autologin).to receive(:Read)
Expand Down

0 comments on commit 8095bbf

Please sign in to comment.