Skip to content

Commit

Permalink
Merge pull request #286 from yast/linux-base-reader
Browse files Browse the repository at this point in the history
Adds a Linux::BaseReader
  • Loading branch information
dgdavid committed May 28, 2021
2 parents ddb509c + 138326b commit 2f0f785
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 111 deletions.
1 change: 1 addition & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ ylib_y2users_clients_DATA = \

ylib_y2users_linuxdir = @ylibdir@/y2users/linux
ylib_y2users_linux_DATA = \
lib/y2users/linux/base_reader.rb \
lib/y2users/linux/local_reader.rb \
lib/y2users/linux/reader.rb \
lib/y2users/linux/writer.rb
Expand Down
111 changes: 111 additions & 0 deletions src/lib/y2users/linux/base_reader.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Copyright (c) [2021] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 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 LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "abstract_method"
require "y2users/config"
require "y2users/parsers/group"
require "y2users/parsers/passwd"
require "y2users/parsers/shadow"
require "users/ssh_authorized_keyring"

module Y2Users
module Linux
# Base class for reading users configuration from the system
class BaseReader
include Yast::Logger

# Generates a new config with the users and groups from the read content
#
# @return [Config]
def read
elements = read_users + read_groups

config = Config.new.attach(elements)

read_passwords(config)
read_authorized_keys(config)

config
end

private

# Parses the content retrieved by {#load_users} and returns a collection of users
#
# @see Parsers::Passwd#parse
# @return [Array<Y2Users::User>]
def read_users
parser = Parsers::Passwd.new
parser.parse(load_users)
end

# @!method load_users
# @return [String] loaded users from the system
abstract_method :load_users

# Parses the content retrieved by {#load_groups} and returns a collection of groups
#
# @see Parsers::Group#parse
# @return [Array<Y2Users::Group>]
def read_groups
parser = Parsers::Group.new
parser.parse(load_groups)
end

# @!method load_groups
# @return [String] loaded groups from the system
abstract_method :load_groups

# Parses the content retrieved by {#load_passwords} and sets user passwords
#
# @see Parsers::Shadow#parse
# @return [Hash<String, Y2Users::Password>]
def read_passwords(config)
parser = Parsers::Shadow.new

passwords = parser.parse(load_passwords)
passwords.each_pair do |name, password|
user = config.users.by_name(name)
if !user
log.warn "Found password for non existing user #{name}."
next
end

user.password = password
end
end

# @!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).read_keys
end
end
end
end
end
85 changes: 30 additions & 55 deletions src/lib/y2users/linux/local_reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,79 +17,54 @@
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "y2users/config"
require "y2users/parsers/group"
require "y2users/parsers/passwd"
require "y2users/parsers/shadow"
require "users/ssh_authorized_keyring"
require "y2users/linux/base_reader"

module Y2Users
module Linux
# Reads local users configuration from the system using /etc files.
class LocalReader
include Yast::Logger

class LocalReader < BaseReader
# Constructor
#
# @param source_dir [String, Pathname] path of source directory for reading files
def initialize(source_dir = "/")
@source_dir = source_dir
end

# Generates a new config with the users and groups from the /etc files
#
# @return [Config]
def read
elements = read_users + read_groups

config = Config.new.attach(elements)

# read passwords after user, as user has to exist in advance
read_passwords(config)

# read authorized keys
read_authorized_keys(config)

config
end

private

# Source directory for reading files content
#
# @see #load_file
# @return [String, Pathname]
attr_reader :source_dir

def read_users
content = File.read(File.join(source_dir, "/etc/passwd"))
parser = Parsers::Passwd.new

parser.parse(content)
# Loads the content of /etc/passwd file
#
# @return [String]
def load_users
load_file("/etc/passwd")
end

def read_groups
content = File.read(File.join(source_dir, "/etc/group"))
parser = Parsers::Group.new

parser.parse(content)
# Loads the content of /etc/group file
#
# @return [String]
def load_groups
load_file("/etc/group")
end

def read_passwords(config)
content = File.read(File.join(source_dir, "/etc/shadow"))
parser = Parsers::Shadow.new

passwords = parser.parse(content)
passwords.each_pair do |name, password|
user = config.users.by_name(name)
if !user
log.warn "Found password for non existing user #{password.name}."
next
end

user.password = password
end
# Loads the content of /etc/shadow file
#
# @return [String]
def load_passwords
load_file("/etc/shadow")
end

def read_authorized_keys(config)
config.users.each do |user|
next unless user.home

user.authorized_keys = Yast::Users::SSHAuthorizedKeyring.new(user.home).read_keys
end
# Loads the content of given file path within the {#source_dir}
#
# @param path [String, Pathname] the path to the file to be read
# @return [String] the content of the read file
def load_file(path)
File.read(File.join(source_dir, path))
end
end
end
Expand Down
85 changes: 29 additions & 56 deletions src/lib/y2users/linux/reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,73 +18,46 @@
# find current contact information at www.suse.com.

require "yast2/execute"
require "y2users/config"
require "y2users/parsers/group"
require "y2users/parsers/passwd"
require "y2users/parsers/shadow"
require "users/ssh_authorized_keyring"
require "y2users/linux/base_reader"

module Y2Users
module Linux
# Reads users configuration from the system using getent utility.
class Reader
include Yast::Logger
# Reads users configuration from the system using `getent` command.
class Reader < BaseReader
private # rubocop:disable Layout/IndentationWidth

# Generates a new config with the users and groups from the system
# Loads entries from `passwd` database
#
# @return [Config]
def read
elements = read_users + read_groups

config = Config.new.attach(elements)

# read passwords after user, as user has to exist in advance
read_passwords(config)

# read authorized keys
read_authorized_keys(config)

config
end

private

def read_users
getent = Yast::Execute.on_target!("/usr/bin/getent", "passwd", stdout: :capture)
parser = Parsers::Passwd.new

parser.parse(getent)
# @see #getent
# @return [String]
def load_users
getent("passwd")
end

def read_groups
getent = Yast::Execute.on_target!("/usr/bin/getent", "group", stdout: :capture)
parser = Parsers::Group.new

parser.parse(getent)
# Loads entries from `group` database
#
# @see #getent
# @return [String]
def load_groups
getent("group")
end

def read_passwords(config)
getent = Yast::Execute.on_target!("/usr/bin/getent", "shadow", stdout: :capture)
parser = Parsers::Shadow.new

passwords = parser.parse(getent)
passwords.each_pair do |name, password|
user = config.users.by_name(name)
if !user
log.warn "Found password for non existing user #{password.name}."
next
end

user.password = password
end
# Loads entries from `shadow` database
#
# @see #getent
# @return [String]
def load_passwords
getent("shadow")
end

def read_authorized_keys(config)
config.users.each do |user|
next unless user.home

user.authorized_keys = Yast::Users::SSHAuthorizedKeyring.new(user.home).read_keys
end
# Executes the `getent` command for getting entries for given Name Service Switch database
#
# @see https://www.man7.org/linux/man-pages/man1/getent.1.html
#
# @param database [String] a database supported by the Name Service Switch libraries
# @return [String] the getent command output
def getent(database)
Yast::Execute.on_target!("/usr/bin/getent", database, stdout: :capture)
end
end
end
Expand Down
1 change: 1 addition & 0 deletions test/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ TESTS = \
lib/y2users/config_element_examples.rb \
lib/y2users/password_helper_test.rb \
lib/y2users/clients/inst_root_first_test.rb \
lib/y2users/linux/base_reader_test.rb \
lib/y2users/linux/local_reader_test.rb \
lib/y2users/linux/reader_test.rb \
lib/y2users/linux/writer_test.rb \
Expand Down

0 comments on commit 2f0f785

Please sign in to comment.