summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changelogs/fragments/78050-replace-spwd.yml3
-rw-r--r--lib/ansible/module_utils/compat/typing.py7
-rw-r--r--lib/ansible/modules/user.py59
-rw-r--r--test/sanity/ignore.txt1
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