anaconda/anaconda-40.22.3.13/pyanaconda/modules/users/users.py
2024-11-14 21:39:56 -08:00

397 lines
14 KiB
Python

#
# Kickstart module for the users module.
#
# Copyright (C) 2018 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# the GNU General Public License v.2, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY expressed or implied, including the implied warranties 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, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
# source code or documentation are not subject to the GNU General Public
# License and may only be used or replicated with the express permission of
# Red Hat, Inc.
#
from pyanaconda.core.configuration.anaconda import conf
from pyanaconda.core.dbus import DBus
from pyanaconda.core.signal import Signal
from pyanaconda.modules.common.base import KickstartService
from pyanaconda.modules.common.constants.services import USERS
from pyanaconda.modules.common.containers import TaskContainer
from pyanaconda.modules.common.structures.user import UserData
from pyanaconda.modules.common.structures.group import GroupData
from pyanaconda.modules.common.structures.sshkey import SshKeyData
from pyanaconda.modules.users.kickstart import UsersKickstartSpecification
from pyanaconda.modules.users.users_interface import UsersInterface
from pyanaconda.modules.users.installation import SetRootPasswordTask, CreateUsersTask, \
CreateGroupsTask, SetSshKeysTask, ConfigureRootPasswordSSHLoginTask
from pyanaconda.anaconda_loggers import get_module_logger
log = get_module_logger(__name__)
class UsersService(KickstartService):
"""The Users service."""
def __init__(self):
super().__init__()
self.can_change_root_password_changed = Signal()
self._can_change_root_password = True
self.root_password_is_set_changed = Signal()
self._root_password = ""
self._root_password_is_crypted = False
self.root_account_locked_changed = Signal()
self._root_account_locked = True
self._root_password_ssh_login_allowed = False
self.root_password_ssh_login_allowed_changed = Signal()
self.users_changed = Signal()
self._users = []
self.groups_changed = Signal()
self._groups = []
self.ssh_keys_changed = Signal()
self._ssh_keys = []
self._rootpw_seen = False
def publish(self):
"""Publish the module."""
TaskContainer.set_namespace(USERS.namespace)
DBus.publish_object(USERS.object_path, UsersInterface(self))
DBus.register_service(USERS.service_name)
@property
def kickstart_specification(self):
"""Return the kickstart specification."""
return UsersKickstartSpecification
def process_kickstart(self, data):
"""Process the kickstart data."""
self.set_root_password(data.rootpw.password, crypted=data.rootpw.isCrypted)
self.set_root_account_locked(data.rootpw.lock)
self.set_root_password_ssh_login_allowed(data.rootpw.allow_ssh)
# make sure the root account is locked unless a password is set in kickstart
if not data.rootpw.password:
log.debug("root specified in kickstart without password, locking account")
self.set_root_account_locked(True)
# if password was set in kickstart it can't be changed by default
if data.rootpw.seen:
self.set_can_change_root_password(False)
self._rootpw_seen = True
user_data_list = []
for user_ksdata in data.user.userList:
user_data_list.append(self._ksdata_to_user_data(user_ksdata))
self.set_users(user_data_list)
group_data_list = []
for group_ksdata in data.group.groupList:
group_data = GroupData()
group_data.name = group_ksdata.name
group_data.set_gid(group_ksdata.gid)
group_data_list.append(group_data)
self.set_groups(group_data_list)
ssh_key_data_list = []
for ssh_key_ksdata in data.sshkey.sshUserList:
ssh_key_data = SshKeyData()
ssh_key_data.key = ssh_key_ksdata.key
ssh_key_data.username = ssh_key_ksdata.username
ssh_key_data_list.append(ssh_key_data)
self.set_ssh_keys(ssh_key_data_list)
# pylint: disable=arguments-differ
def setup_kickstart(self, data):
"""Set up the kickstart data."""
data.rootpw.password = self._root_password
data.rootpw.isCrypted = self._root_password_is_crypted
data.rootpw.lock = self.root_account_locked
data.rootpw.allow_ssh = self.root_password_ssh_login_allowed
for user_data in self.users:
data.user.userList.append(self._user_data_to_ksdata(data.UserData(),
user_data))
for group_data in self.groups:
group_ksdata = data.GroupData()
group_ksdata.name = group_data.name
group_ksdata.gid = group_data.get_gid()
data.group.groupList.append(group_ksdata)
for ssh_key_data in self.ssh_keys:
ssh_key_ksdata = data.SshKeyData()
ssh_key_ksdata.key = ssh_key_data.key
ssh_key_ksdata.username = ssh_key_data.username
data.sshkey.sshUserList.append(ssh_key_ksdata)
def configure_groups_with_task(self):
"""Return the user group configuration task.
:returns: a user group configuration task
"""
return CreateGroupsTask(
sysroot=conf.target.system_root,
group_data_list=self.groups
)
def configure_users_with_task(self):
"""Return the user configuration task.
:returns: a user configuration task
"""
return CreateUsersTask(
sysroot=conf.target.system_root,
user_data_list=self.users
)
def set_root_password_with_task(self):
"""Return the root password configuration task.
:returns: a root password configuration task
"""
return SetRootPasswordTask(
sysroot=conf.target.system_root,
password=self.root_password,
crypted=self.root_password_is_crypted,
locked=self.root_account_locked
)
def set_ssh_keys_with_task(self):
"""Return the SSH key configuration task.
:returns: o SSH key configuration task
"""
return SetSshKeysTask(
sysroot=conf.target.system_root,
ssh_key_data_list=self.ssh_keys
)
def configure_root_password_ssh_login_with_task(self):
"""Return the root password SSH login configuration task.
:returns: a root password SSH login configuration task
"""
return ConfigureRootPasswordSSHLoginTask(
sysroot=conf.target.system_root,
password_allowed=self.root_password_ssh_login_allowed
)
def install_with_tasks(self):
"""Return the installation tasks of this module.
:returns: list of tasks
"""
return [
self.configure_groups_with_task(),
self.configure_users_with_task(),
self.set_root_password_with_task(),
self.set_ssh_keys_with_task(),
self.configure_root_password_ssh_login_with_task()
]
def _ksdata_to_user_data(self, user_ksdata):
"""Apply kickstart user command data to UserData instance.
:param user_ksdata: data for the kickstart user command
:return: UserData instance with kickstart data applied
"""
user_data = UserData()
user_data.name = user_ksdata.name
user_data.groups = user_ksdata.groups
user_data.set_uid(user_ksdata.uid)
user_data.set_gid(user_ksdata.gid)
user_data.homedir = user_ksdata.homedir
user_data.password = user_ksdata.password
user_data.is_crypted = user_ksdata.isCrypted
user_data.lock = user_ksdata.lock
# make sure the user account is locked by default unless a password
# is set in kickstart
if not user_ksdata.password:
log.debug("user (%s) specified in kickstart without password, locking account",
user_ksdata.name)
user_data.lock = True
user_data.shell = user_ksdata.shell
user_data.gecos = user_ksdata.gecos
return user_data
def _user_data_to_ksdata(self, user_ksdata, user_data):
"""Convert UserData instance to kickstart user command data.
:param user_ksdata: UserData instance from Kickstart
:param user_data: our UserData instance
:return: kickstart user command data for a single user
"""
user_ksdata.name = user_data.name
user_ksdata.groups = user_data.groups
user_ksdata.uid = user_data.get_uid()
user_ksdata.gid = user_data.get_gid()
user_ksdata.homedir = user_data.homedir
user_ksdata.password = user_data.password
user_ksdata.isCrypted = user_data.is_crypted
user_ksdata.lock = user_data.lock
user_ksdata.shell = user_data.shell
user_ksdata.gecos = user_data.gecos
return user_ksdata
@property
def users(self):
"""List of UserData instances, one per user."""
return self._users
def set_users(self, users):
"""Set the list of UserData instances, one per user."""
self._users = users
self.users_changed.emit()
log.debug("A new user list has been set: %s", self._users)
@property
def groups(self):
"""List of GroupData instances, one per group."""
return self._groups
def set_groups(self, groups):
"""Set the list of GroupData instances, one per group."""
self._groups = groups
self.groups_changed.emit()
log.debug("A new group list has been set: %s", self._groups)
@property
def ssh_keys(self):
"""List of SshKeyData instances, one per ssh key."""
return self._ssh_keys
def set_ssh_keys(self, ssh_keys):
"""Set the list of SshKeyData instances, one per ssh keys."""
self._ssh_keys = ssh_keys
self.ssh_keys_changed.emit()
log.debug("A new ssh key list has been set: %s", self._ssh_keys)
@property
def can_change_root_password(self):
return self._can_change_root_password
def set_can_change_root_password(self, can_change_root_password):
self._can_change_root_password = can_change_root_password
self.can_change_root_password_changed.emit()
log.debug("Can change root password state changed: %s.", can_change_root_password)
@property
def root_password(self):
"""The root password.
:returns: root password (might be crypted)
:rtype: str
"""
return self._root_password
@property
def root_password_is_crypted(self):
"""Is the root password crypted ?
:returns: if root password is crypted
:rtype: bool
"""
return self._root_password_is_crypted
def set_root_password(self, root_password, crypted):
"""Set the crypted root password.
NOTE: Setting password == "" is equivalent to
calling clear_root_password().
:param str root_password: root password
:param bool crypted: if the root password is crypted
"""
if root_password == "":
self._root_password = ""
self._root_password_is_crypted = False
self.set_root_account_locked(True)
self.root_password_is_set_changed.emit()
log.debug("Root password cleared.")
else:
self._root_password = root_password
self._root_password_is_crypted = crypted
self.root_password_is_set_changed.emit()
log.debug("Root password set.")
def clear_root_password(self):
"""Clear any set root password."""
self.set_root_password("", False)
@property
def root_password_is_set(self):
"""Is the root password set ?"""
return bool(self._root_password)
def set_root_account_locked(self, locked):
"""Lock or unlock the root account.
:param bool locked: True id the account should be locked, False otherwise.
"""
self._root_account_locked = locked
self.root_account_locked_changed.emit()
if locked:
log.debug("Root account has been locked.")
else:
log.debug("Root account has been unlocked.")
@property
def root_account_locked(self):
"""Is the root account locked ?"""
return self._root_account_locked
def set_root_password_ssh_login_allowed(self, root_password_ssh_login_allowed):
"""Allow/disable root login via SSH with password.
(Login as root with key is always allowed)
param bool root_password_ssh_login_allowed: True to allow, False to disallow
"""
self._root_password_ssh_login_allowed = root_password_ssh_login_allowed
self.root_password_ssh_login_allowed_changed.emit()
if root_password_ssh_login_allowed:
log.debug("SSH login as root with password will be allowed.")
else:
log.debug("SSH login as root with password will not be allowed.")
@property
def root_password_ssh_login_allowed(self):
"""Is logging in as root via SSH with password allowed ?"""
return self._root_password_ssh_login_allowed
@property
def check_admin_user_exists(self):
"""Reports if at least one admin user exists.
- an unlocked root account is considered to be an admin user
- an unlocked user account that is member of the group "wheel"
is considered to be an admin user
:return: if at least one admin user exists
"""
# any root set from kickstart is fine
if self._rootpw_seen:
return True
# if not set by kickstart root must not be
# locked to be cosnidered admin
elif self.root_password and not self.root_account_locked:
return True
# let's check all users
for user in self.users:
if not user.lock:
if "wheel" in user.groups:
return True
# no admin user found
return False