diff options
Diffstat (limited to 'lib/ansible/modules/user.py')
-rw-r--r-- | lib/ansible/modules/user.py | 174 |
1 files changed, 75 insertions, 99 deletions
diff --git a/lib/ansible/modules/user.py b/lib/ansible/modules/user.py index 6d465b04..2fc4e473 100644 --- a/lib/ansible/modules/user.py +++ b/lib/ansible/modules/user.py @@ -28,12 +28,11 @@ options: comment: description: - Optionally sets the description (aka I(GECOS)) of user account. - - On macOS, this defaults to the O(name) option. type: str hidden: description: - macOS only, optionally hide the user from the login window and system preferences. - - The default will be V(true) if the O(system) option is used. + - The default will be C(true) if the I(system) option is used. type: bool version_added: "2.6" non_unique: @@ -50,29 +49,28 @@ options: group: description: - Optionally sets the user's primary group (takes a group name). - - On macOS, this defaults to V('staff') type: str groups: description: - - A list of supplementary groups which the user is also a member of. - - By default, the user is removed from all other groups. Configure O(append) to modify this. - - When set to an empty string V(''), + - List of groups user will be added to. + - By default, the user is removed from all other groups. Configure C(append) to modify this. + - When set to an empty string C(''), the user is removed from all groups except the primary group. - Before Ansible 2.3, the only input format allowed was a comma separated string. type: list elements: str append: description: - - If V(true), add the user to the groups specified in O(groups). - - If V(false), user will only be added to the groups specified in O(groups), + - If C(true), add the user to the groups specified in C(groups). + - If C(false), user will only be added to the groups specified in C(groups), removing them from all other groups. type: bool default: no shell: description: - Optionally set the user's shell. - - On macOS, before Ansible 2.5, the default shell for non-system users was V(/usr/bin/false). - Since Ansible 2.5, the default shell for non-system users on macOS is V(/bin/bash). + - On macOS, before Ansible 2.5, the default shell for non-system users was C(/usr/bin/false). + Since Ansible 2.5, the default shell for non-system users on macOS is C(/bin/bash). - See notes for details on how other operating systems determine the default shell by the underlying tool. type: str @@ -83,7 +81,7 @@ options: skeleton: description: - Optionally set a home skeleton directory. - - Requires O(create_home) option! + - Requires C(create_home) option! type: str version_added: "2.0" password: @@ -92,51 +90,46 @@ options: - B(Linux/Unix/POSIX:) Enter the hashed password as the value. - See L(FAQ entry,https://docs.ansible.com/ansible/latest/reference_appendices/faq.html#how-do-i-generate-encrypted-passwords-for-the-user-module) for details on various ways to generate the hash of a password. - - To create an account with a locked/disabled password on Linux systems, set this to V('!') or V('*'). - - To create an account with a locked/disabled password on OpenBSD, set this to V('*************'). + - To create an account with a locked/disabled password on Linux systems, set this to C('!') or C('*'). + - To create an account with a locked/disabled password on OpenBSD, set this to C('*************'). - B(OS X/macOS:) Enter the cleartext password as the value. Be sure to take relevant security precautions. - - On macOS, the password specified in the C(password) option will always be set, regardless of whether the user account already exists or not. - - When the password is passed as an argument, the C(user) module will always return changed to C(true) for macOS systems. - Since macOS no longer provides access to the hashed passwords directly. type: str state: description: - Whether the account should exist or not, taking action if the state is different from what is stated. - - See this L(FAQ entry,https://docs.ansible.com/ansible/latest/reference_appendices/faq.html#running-on-macos-as-a-target) - for additional requirements when removing users on macOS systems. type: str choices: [ absent, present ] default: present create_home: description: - - Unless set to V(false), a home directory will be made for the user + - Unless set to C(false), a home directory will be made for the user when the account is created or if the home directory does not exist. - - Changed from O(createhome) to O(create_home) in Ansible 2.5. + - Changed from C(createhome) to C(create_home) in Ansible 2.5. type: bool default: yes aliases: [ createhome ] move_home: description: - - "If set to V(true) when used with O(home), attempt to move the user's old home + - "If set to C(true) when used with C(home: ), attempt to move the user's old home directory to the specified directory if it isn't there already and the old home exists." type: bool default: no system: description: - - When creating an account O(state=present), setting this to V(true) makes the user a system account. + - When creating an account C(state=present), setting this to C(true) makes the user a system account. - This setting cannot be changed on existing users. type: bool default: no force: description: - - This only affects O(state=absent), it forces removal of the user and associated directories on supported platforms. + - This only affects C(state=absent), it forces removal of the user and associated directories on supported platforms. - The behavior is the same as C(userdel --force), check the man page for C(userdel) on your system for details and support. - - When used with O(generate_ssh_key=yes) this forces an existing key to be overwritten. + - When used with C(generate_ssh_key=yes) this forces an existing key to be overwritten. type: bool default: no remove: description: - - This only affects O(state=absent), it attempts to remove directories associated with the user. + - This only affects C(state=absent), it attempts to remove directories associated with the user. - The behavior is the same as C(userdel --remove), check the man page for details and support. type: bool default: no @@ -147,7 +140,7 @@ options: generate_ssh_key: description: - Whether to generate a SSH key for the user in question. - - This will B(not) overwrite an existing SSH key unless used with O(force=yes). + - This will B(not) overwrite an existing SSH key unless used with C(force=yes). type: bool default: no version_added: "0.9" @@ -169,7 +162,7 @@ options: description: - Optionally specify the SSH key filename. - If this is a relative filename then it will be relative to the user's home directory. - - This parameter defaults to V(.ssh/id_rsa). + - This parameter defaults to I(.ssh/id_rsa). type: path version_added: "0.9" ssh_key_comment: @@ -186,8 +179,8 @@ options: version_added: "0.9" update_password: description: - - V(always) will update passwords if they differ. - - V(on_create) will only set the password for newly created users. + - C(always) will update passwords if they differ. + - C(on_create) will only set the password for newly created users. type: str choices: [ always, on_create ] default: always @@ -205,7 +198,7 @@ options: - Lock the password (C(usermod -L), C(usermod -U), C(pw lock)). - Implementation differs by platform. This option does not always mean the user cannot login using other methods. - This option does not disable the user, only lock the password. - - This must be set to V(False) in order to unlock a currently locked password. The absence of this parameter will not unlock a password. + - This must be set to C(False) in order to unlock a currently locked password. The absence of this parameter will not unlock a password. - Currently supported on Linux, FreeBSD, DragonFlyBSD, NetBSD, OpenBSD. type: bool version_added: "2.6" @@ -223,25 +216,28 @@ options: profile: description: - Sets the profile of the user. + - Does nothing when used with other platforms. - Can set multiple profiles using comma separation. - - To delete all the profiles, use O(profile=''). - - Currently supported on Illumos/Solaris. Does nothing when used with other platforms. + - To delete all the profiles, use C(profile=''). + - Currently supported on Illumos/Solaris. type: str version_added: "2.8" authorization: description: - Sets the authorization of the user. + - Does nothing when used with other platforms. - Can set multiple authorizations using comma separation. - - To delete all authorizations, use O(authorization=''). - - Currently supported on Illumos/Solaris. Does nothing when used with other platforms. + - To delete all authorizations, use C(authorization=''). + - Currently supported on Illumos/Solaris. type: str version_added: "2.8" role: description: - Sets the role of the user. + - Does nothing when used with other platforms. - Can set multiple roles using comma separation. - - To delete all roles, use O(role=''). - - Currently supported on Illumos/Solaris. Does nothing when used with other platforms. + - To delete all roles, use C(role=''). + - Currently supported on Illumos/Solaris. type: str version_added: "2.8" password_expire_max: @@ -256,17 +252,12 @@ options: - Supported on Linux only. type: int version_added: "2.11" - password_expire_warn: - description: - - Number of days of warning before password expires. - - Supported on Linux only. - type: int - version_added: "2.16" umask: description: - Sets the umask of the user. - - Currently supported on Linux. Does nothing when used with other platforms. - - Requires O(local) is omitted or V(False). + - Does nothing when used with other platforms. + - Currently supported on Linux. + - Requires C(local) is omitted or False. type: str version_added: "2.12" extends_documentation_fragment: action_common_attributes @@ -347,17 +338,12 @@ EXAMPLES = r''' ansible.builtin.user: name: pushkar15 password_expire_min: 5 - -- name: Set number of warning days for password expiration - ansible.builtin.user: - name: jane157 - password_expire_warn: 30 ''' RETURN = r''' append: description: Whether or not to append the user to groups. - returned: When O(state) is V(present) and the user exists + returned: When state is C(present) and the user exists type: bool sample: True comment: @@ -372,7 +358,7 @@ create_home: sample: True force: description: Whether or not a user account was forcibly deleted. - returned: When O(state) is V(absent) and user exists + returned: When I(state) is C(absent) and user exists type: bool sample: False group: @@ -382,17 +368,17 @@ group: sample: 1001 groups: description: List of groups of which the user is a member. - returned: When O(groups) is not empty and O(state) is V(present) + returned: When I(groups) is not empty and I(state) is C(present) type: str sample: 'chrony,apache' home: description: "Path to user's home directory." - returned: When O(state) is V(present) + returned: When I(state) is C(present) type: str sample: '/home/asmith' move_home: description: Whether or not to move an existing home directory. - returned: When O(state) is V(present) and user exists + returned: When I(state) is C(present) and user exists type: bool sample: False name: @@ -402,32 +388,32 @@ name: sample: asmith password: description: Masked value of the password. - returned: When O(state) is V(present) and O(password) is not empty + returned: When I(state) is C(present) and I(password) is not empty type: str sample: 'NOT_LOGGING_PASSWORD' remove: description: Whether or not to remove the user account. - returned: When O(state) is V(absent) and user exists + returned: When I(state) is C(absent) and user exists type: bool sample: True shell: description: User login shell. - returned: When O(state) is V(present) + returned: When I(state) is C(present) type: str sample: '/bin/bash' ssh_fingerprint: description: Fingerprint of generated SSH key. - returned: When O(generate_ssh_key) is V(True) + returned: When I(generate_ssh_key) is C(True) type: str sample: '2048 SHA256:aYNHYcyVm87Igh0IMEDMbvW0QDlRQfE0aJugp684ko8 ansible-generated on host (RSA)' ssh_key_file: description: Path to generated SSH private key file. - returned: When O(generate_ssh_key) is V(True) + returned: When I(generate_ssh_key) is C(True) type: str sample: /home/asmith/.ssh/id_rsa ssh_public_key: description: Generated SSH public key file. - returned: When O(generate_ssh_key) is V(True) + returned: When I(generate_ssh_key) is C(True) type: str sample: > 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC95opt4SPEC06tOYsJQJIuN23BbLMGmYo8ysVZQc4h2DZE9ugbjWWGS1/pweUGjVstgzMkBEeBCByaEf/RJKNecKRPeGd2Bw9DCj/bn5Z6rGfNENKBmo @@ -445,18 +431,30 @@ stdout: sample: system: description: Whether or not the account is a system account. - returned: When O(system) is passed to the module and the account does not exist + returned: When I(system) is passed to the module and the account does not exist type: bool sample: True uid: description: User ID of the user account. - returned: When O(uid) is passed to the module + returned: When I(uid) is passed to the module type: int sample: 1044 +password_expire_max: + description: Maximum number of days during which a password is valid. + returned: When user exists + type: int + sample: 20 +password_expire_min: + description: Minimum number of days between password change + returned: When user exists + type: int + sample: 20 ''' +import ctypes import ctypes.util +import errno import grp import calendar import os @@ -471,7 +469,7 @@ import time import math from ansible.module_utils import distro -from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text +from ansible.module_utils._text import to_bytes, to_native, to_text from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.locale import get_best_parsable_locale from ansible.module_utils.common.sys_info import get_platform_subclass @@ -576,7 +574,6 @@ class User(object): self.role = module.params['role'] self.password_expire_max = module.params['password_expire_max'] self.password_expire_min = module.params['password_expire_min'] - self.password_expire_warn = module.params['password_expire_warn'] self.umask = module.params['umask'] if self.umask is not None and self.local: @@ -870,7 +867,7 @@ class User(object): if current_groups and not self.append: groups_need_mod = True else: - groups = self.get_groups_set(remove_existing=False, names_only=True) + groups = self.get_groups_set(remove_existing=False) group_diff = set(current_groups).symmetric_difference(groups) if group_diff: @@ -916,8 +913,7 @@ class User(object): if self.expires is not None: - current_expires = self.user_password()[1] or '0' - current_expires = int(current_expires) + current_expires = int(self.user_password()[1]) if self.expires < time.gmtime(0): if current_expires >= 0: @@ -1012,22 +1008,16 @@ class User(object): except (ValueError, KeyError): return list(grp.getgrnam(group)) - def get_groups_set(self, remove_existing=True, names_only=False): + def get_groups_set(self, remove_existing=True): if self.groups is None: return None info = self.user_info() groups = set(x.strip() for x in self.groups.split(',') if x) - group_names = set() for g in groups.copy(): if not self.group_exists(g): self.module.fail_json(msg="Group %s does not exist" % (g)) - group_info = self.group_info(g) - if info and remove_existing and group_info[2] == info[3]: + if info and remove_existing and self.group_info(g)[2] == info[3]: groups.remove(g) - elif names_only: - group_names.add(group_info[0]) - if names_only: - return group_names return groups def user_group_membership(self, exclude_primary=True): @@ -1094,7 +1084,6 @@ class User(object): def set_password_expire(self): min_needs_change = self.password_expire_min is not None max_needs_change = self.password_expire_max is not None - warn_needs_change = self.password_expire_warn is not None if HAVE_SPWD: try: @@ -1104,9 +1093,8 @@ class User(object): min_needs_change &= self.password_expire_min != shadow_info.sp_min max_needs_change &= self.password_expire_max != shadow_info.sp_max - warn_needs_change &= self.password_expire_warn != shadow_info.sp_warn - if not (min_needs_change or max_needs_change or warn_needs_change): + if not (min_needs_change or max_needs_change): return (None, '', '') # target state already reached command_name = 'chage' @@ -1115,8 +1103,6 @@ class User(object): cmd.extend(["-m", self.password_expire_min]) if max_needs_change: cmd.extend(["-M", self.password_expire_max]) - if warn_needs_change: - cmd.extend(["-W", self.password_expire_warn]) cmd.append(self.name) return self.execute_command(cmd) @@ -1291,7 +1277,7 @@ class User(object): else: skeleton = '/etc/skel' - if os.path.exists(skeleton) and skeleton != os.devnull: + if os.path.exists(skeleton): try: shutil.copytree(skeleton, path, symlinks=True) except OSError as e: @@ -1537,7 +1523,7 @@ class FreeBsdUser(User): if self.groups is not None: current_groups = self.user_group_membership() - groups = self.get_groups_set(names_only=True) + groups = self.get_groups_set() group_diff = set(current_groups).symmetric_difference(groups) groups_need_mod = False @@ -1560,8 +1546,7 @@ class FreeBsdUser(User): if self.expires is not None: - current_expires = self.user_password()[1] or '0' - current_expires = int(current_expires) + current_expires = int(self.user_password()[1]) # If expiration is negative or zero and the current expiration is greater than zero, disable expiration. # In OpenBSD, setting expiration to zero disables expiration. It does not expire the account. @@ -1732,7 +1717,7 @@ class OpenBSDUser(User): if current_groups and not self.append: groups_need_mod = True else: - groups = self.get_groups_set(names_only=True) + groups = self.get_groups_set() group_diff = set(current_groups).symmetric_difference(groups) if group_diff: @@ -1908,7 +1893,7 @@ class NetBSDUser(User): if current_groups and not self.append: groups_need_mod = True else: - groups = self.get_groups_set(names_only=True) + groups = self.get_groups_set() group_diff = set(current_groups).symmetric_difference(groups) if group_diff: @@ -2142,7 +2127,7 @@ class SunOS(User): if self.groups is not None: current_groups = self.user_group_membership() - groups = self.get_groups_set(names_only=True) + groups = self.get_groups_set() group_diff = set(current_groups).symmetric_difference(groups) groups_need_mod = False @@ -2419,7 +2404,7 @@ class DarwinUser(User): current = set(self._list_user_groups()) if self.groups is not None: - target = self.get_groups_set(names_only=True) + target = set(self.groups.split(',')) else: target = set([]) @@ -2513,14 +2498,6 @@ class DarwinUser(User): if rc != 0: self.module.fail_json(msg='Cannot create user "%s".' % self.name, err=err, out=out, rc=rc) - # Make the Gecos (alias display name) default to username - if self.comment is None: - self.comment = self.name - - # Make user group default to 'staff' - if self.group is None: - self.group = 'staff' - self._make_group_numerical() if self.uid is None: self.uid = str(self._get_next_uid(self.system)) @@ -2711,7 +2688,7 @@ class AIX(User): if current_groups and not self.append: groups_need_mod = True else: - groups = self.get_groups_set(names_only=True) + groups = self.get_groups_set() group_diff = set(current_groups).symmetric_difference(groups) if group_diff: @@ -2909,7 +2886,7 @@ class HPUX(User): if current_groups and not self.append: groups_need_mod = True else: - groups = self.get_groups_set(remove_existing=False, names_only=True) + groups = self.get_groups_set(remove_existing=False) group_diff = set(current_groups).symmetric_difference(groups) if group_diff: @@ -3119,7 +3096,6 @@ def main(): login_class=dict(type='str'), password_expire_max=dict(type='int', no_log=False), password_expire_min=dict(type='int', no_log=False), - password_expire_warn=dict(type='int', no_log=False), # following options are specific to macOS hidden=dict(type='bool'), # following options are specific to selinux |