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

377 lines
10 KiB
Python

#
# DBus structures for describing the user.
#
# Copyright (C) 2019 Red Hat, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# 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, see <http://www.gnu.org/licenses/>.
#
from dasbus.structure import DBusData
from dasbus.structure import generate_string_from_data
from dasbus.typing import * # pylint: disable=wildcard-import
from pyanaconda.core.constants import ID_MODE_USE_DEFAULT, ID_MODE_USE_VALUE
__all__ = ["UserData"]
class UserData(DBusData):
"""User data."""
def __init__(self):
self._name = ""
self._uid = 0
self._uid_mode = ID_MODE_USE_DEFAULT
self._groups = list()
self._gid = 0
self._gid_mode = ID_MODE_USE_DEFAULT
self._homedir = ""
self._password = ""
self._is_crypted = True
self._lock = False
self._shell = ""
self._gecos = ""
@property
def name(self) -> Str:
"""Username."
For example: 'user'
Should comply with the usual limitations for Linux usernames.
:return: a username
:rtype: str
"""
return self._name
@name.setter
def name(self, name: Str):
self._name = name
@property
def uid_mode(self) -> Str:
"""Mode of UID.
Contains a string describing the mode of the user's UID: Use the value or default.
Possible values are:
- "ID_MODE_USE_VALUE"
- "ID_MODE_USE_DEFAULT"
:return: the mode
:rtype str:
"""
return self._uid_mode
@uid_mode.setter
def uid_mode(self, status: Str):
self._uid_mode = status
@property
def uid(self) -> UInt32:
"""The users UID.
If ignored due to uid_mode, this defaults to the next available non-system UID.
For example: 1234
:return: user's UID
:rtype: int
"""
return self._uid
@uid.setter
def uid(self, uid: UInt32):
self._uid = uid
def get_uid(self):
"""Return a UID value which can be a number or None.
Prefer using this method instead of directly reading uid and uid_mode.
:return: UID or None if not set
:rtype: int or None
"""
if self._uid_mode == ID_MODE_USE_DEFAULT:
return None
else:
return self._uid
def set_uid(self, new_uid):
"""Set UID value and mode from a value which can be None.
Prefer using this method instead of directly setting uid and uid_mode.
:param new_uid: new UID
:type new_uid: int or None
"""
if new_uid is not None:
self._uid = new_uid
self._uid_mode = ID_MODE_USE_VALUE
else:
self._uid = 0
self._uid_mode = ID_MODE_USE_DEFAULT
@property
def groups(self) -> List[Str]:
"""List of additional groups (in additional to the default group named after
the user) the user should be a member of. Any groups that do not already
exist will be created.
For example: ['mock', 'dialout']
:return: a list of groups the user should be a member of
:rtype: a list of str
"""
return self._groups
@groups.setter
def groups(self, groups: List[Str]):
self._groups = groups
@property
def gid_mode(self) -> Str:
"""Mode of GID.
Contains a string describing the mode of the user's GID: Use the value or default.
Possible values are:
- "ID_MODE_USE_VALUE"
- "ID_MODE_USE_DEFAULT"
:return: the mode
:rtype str:
"""
return self._gid_mode
@gid_mode.setter
def gid_mode(self, status: Str):
self._gid_mode = status
@property
def gid(self) -> UInt32:
"""The GID of the user's primary group.
If ignored due to gid_mode, defaults to the next available non-system GID.
For example: 1234
:return: primary group GID
:rtype: int
"""
return self._gid
@gid.setter
def gid(self, gid: UInt32):
self._gid = gid
def get_gid(self):
"""Return a GID value which can be a number or None.
Prefer using this method instead of directly reading gid and gid_mode.
:return: GID or None if not set
:rtype: int or None
"""
if self._gid_mode == ID_MODE_USE_DEFAULT:
return None
else:
return self._gid
def set_gid(self, new_gid):
"""Set GID value and mode from a value which can be None.
Prefer using this method instead of directly writing gid and gid_mode.
:param new_gid: new GID
:type new_gid: int or None
"""
if new_gid is not None:
self._gid = new_gid
self._gid_mode = ID_MODE_USE_VALUE
else:
self._gid = 0
self._gid_mode = ID_MODE_USE_DEFAULT
@property
def homedir(self) -> Str:
"""The home directory of the user.
If not provided, this defaults to /home/.
For example: 'user_home'
:return: home directory of the user
:rtype: str
"""
return self._homedir
@homedir.setter
def homedir(self, homedir: Str):
self._homedir = homedir
@property
def password(self) -> Str:
"""The user password.
If not provided, the account will be locked by default. If this is set,
the password argument is assumed to already be encrypted.
If the plaintext property has been set, it has the opposite effect,
the user password is assumed to not be encrypted.
To create an encrypted password you can use python:
python3 -c 'import crypt; print(crypt.crypt("My Password", "$y$j9T$My Sault"))'
This will compute a hash of your password using the yescrypt hasing method and
your provided salt.
If the yescrypt method is not supported by your system, you can use the
sha512crypt hashing method:
python3 -c 'import crypt; print(crypt.crypt("My Password", "$6$My Sault"))'
This will compute a hash of your password using the sha512crypt hasing method
and your provided salt.
For example: "CorrectHorseBatteryStaple"
:return: user password
:rtype: str
"""
return self._password
@password.setter
def password(self, password: Str):
self._password = password
@property
def is_crypted(self) -> Bool:
"""Reports if the password is already crypted.
A password is considered to be crypted by default.
For example: True
:return: if password is already crypted
:rtype: bool
"""
return self._is_crypted
@is_crypted.setter
def is_crypted(self, is_crypted: Bool):
self._is_crypted = is_crypted
@property
def lock(self) -> Bool:
"""If lock is True, this user account will be locked be locked.
That is, the user will not be able to login from the console.
For example: False
:rtype: if the user account should be locked
:rtype: bool
"""
return self._lock
@lock.setter
def lock(self, lock: Bool):
self._lock = lock
@property
def shell(self) -> Str:
"""The users login shell.
If not provided this defaults to the system default.
For example: "/bin/bash"
:return: user login shell
:rtype: str
"""
return self._shell
@shell.setter
def shell(self, shell: Str):
self._shell = shell
@property
def gecos(self) -> Str:
"""Provides the GECOS information for the user.
This is a string of various system-specific fields separated by a comma.
It is frequently used to specify the user's full name, office number, and the like.
See man 5 passwd for more details.
For examples: "foo"
:return: GECOS information for the user
:rtype: str
"""
return self._gecos
@gecos.setter
def gecos(self, gecos: Str):
self._gecos = gecos
def __eq__(self, other_instance):
"""Compare if this UserData instance is the same as other UserData instance.
As we don't really do much validation for the user specifications, including
duplication checks and will just warn about errors during user creation then
simply checking the user name seems like a good enough way to tell two users apart.
Different settings other than username are just two variants of the same user.
Two users with different username are two different users.
:param other_instance: another UserData instance
:return: if the other instance seems to be functionally the same as this one
"""
return self.name == other_instance.name
def __repr__(self):
"""Describe the user for easy debugging.
As there are many fields many of which might not be set,
we only try to list the values that are set.
:return: a string describing the UserData instance
:rtype: str
"""
return generate_string_from_data(
self, skip=["password"], add={"password_set": bool(self.password)}
)
def has_admin_priviledges(self):
"""Report if the user described by this structure is an admin.
Admin users are members of the "wheel" group.
:return: True if user is admin, False otherwise
:rtype: bool
"""
return "wheel" in self._groups
def set_admin_priviledges(self, admin):
"""Set if the user should be an admin.
This effectively means adding/removing the "wheel" group from users group list.
"""
if admin and "wheel" not in self._groups:
self._groups.append("wheel")
elif not admin and "wheel" in self._groups:
self._groups.remove("wheel")