diff options
-rw-r--r-- | changelogs/fragments/78050-replace-spwd.yml | 3 | ||||
-rw-r--r-- | lib/ansible/module_utils/compat/typing.py | 7 | ||||
-rw-r--r-- | lib/ansible/modules/user.py | 59 | ||||
-rw-r--r-- | test/sanity/ignore.txt | 1 |
4 files changed, 48 insertions, 22 deletions
diff --git a/changelogs/fragments/78050-replace-spwd.yml b/changelogs/fragments/78050-replace-spwd.yml new file mode 100644 index 0000000000..8dc67117ac --- /dev/null +++ b/changelogs/fragments/78050-replace-spwd.yml @@ -0,0 +1,3 @@ +bugfixes: +- user module - Replace uses of the deprecated ``spwd`` python module with ctypes + (https://github.com/ansible/ansible/pull/78050) diff --git a/lib/ansible/module_utils/compat/typing.py b/lib/ansible/module_utils/compat/typing.py index c361a86717..27b25f7738 100644 --- a/lib/ansible/module_utils/compat/typing.py +++ b/lib/ansible/module_utils/compat/typing.py @@ -16,3 +16,10 @@ try: from typing import * # type: ignore[misc] except Exception: # pylint: disable=broad-except pass + + +try: + cast +except NameError: + def cast(typ, val): # type: ignore[no-redef] + return val diff --git a/lib/ansible/modules/user.py b/lib/ansible/modules/user.py index 4106d2b829..a34bef22fd 100644 --- a/lib/ansible/modules/user.py +++ b/lib/ansible/modules/user.py @@ -451,6 +451,8 @@ password_expire_min: ''' +import ctypes +import ctypes.util import errno import grp import calendar @@ -470,17 +472,44 @@ 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 +import ansible.module_utils.compat.typing as t + + +class StructSpwdType(ctypes.Structure): + _fields_ = [ + ('sp_namp', ctypes.c_char_p), + ('sp_pwdp', ctypes.c_char_p), + ('sp_lstchg', ctypes.c_long), + ('sp_min', ctypes.c_long), + ('sp_max', ctypes.c_long), + ('sp_warn', ctypes.c_long), + ('sp_inact', ctypes.c_long), + ('sp_expire', ctypes.c_long), + ('sp_flag', ctypes.c_ulong), + ] + try: - import spwd + _LIBC = ctypes.cdll.LoadLibrary( + t.cast( + str, + ctypes.util.find_library('c') + ) + ) + _LIBC.getspnam.argtypes = (ctypes.c_char_p,) + _LIBC.getspnam.restype = ctypes.POINTER(StructSpwdType) HAVE_SPWD = True -except ImportError: +except AttributeError: HAVE_SPWD = False _HASH_RE = re.compile(r'[^a-zA-Z0-9./=]') +def getspnam(b_name): + return _LIBC.getspnam(b_name).contents + + class User(object): """ This is a generic User manipulation class that is subclassed @@ -1053,16 +1082,10 @@ class User(object): if HAVE_SPWD: try: - shadow_info = spwd.getspnam(self.name) - except KeyError: + shadow_info = getspnam(to_bytes(self.name)) + except ValueError: return None, '', '' - except OSError as e: - # Python 3.6 raises PermissionError instead of KeyError - # Due to absence of PermissionError in python2.7 need to check - # errno - if e.errno in (errno.EACCES, errno.EPERM, errno.ENOENT): - return None, '', '' - raise + min_needs_change &= self.password_expire_min != shadow_info.sp_min max_needs_change &= self.password_expire_max != shadow_info.sp_max @@ -1084,18 +1107,12 @@ class User(object): expires = '' if HAVE_SPWD: try: - passwd = spwd.getspnam(self.name)[1] - expires = spwd.getspnam(self.name)[7] + shadow_info = getspnam(to_bytes(self.name)) + passwd = to_native(shadow_info.sp_pwdp) + expires = shadow_info.sp_expire return passwd, expires - except KeyError: + except ValueError: return passwd, expires - except OSError as e: - # Python 3.6 raises PermissionError instead of KeyError - # Due to absence of PermissionError in python2.7 need to check - # errno - if e.errno in (errno.EACCES, errno.EPERM, errno.ENOENT): - return passwd, expires - raise if not self.user_exists(): return passwd, expires diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index 75cb27018d..1bae64c168 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -58,7 +58,6 @@ lib/ansible/modules/sysvinit.py validate-modules:return-syntax-error lib/ansible/modules/uri.py validate-modules:doc-required-mismatch lib/ansible/modules/user.py validate-modules:doc-default-does-not-match-spec lib/ansible/modules/user.py validate-modules:use-run-command-not-popen -lib/ansible/modules/user.py import-3.11 # spwd is deprecated and will be removed in Python 3.13 lib/ansible/modules/yum.py pylint:disallowed-name lib/ansible/modules/yum.py validate-modules:parameter-invalid lib/ansible/modules/yum_repository.py validate-modules:doc-default-does-not-match-spec |