leapp-repository/SOURCES/0008-pam_userdb-migrate-backend-database.patch

535 lines
22 KiB
Diff
Raw Normal View History

2024-11-25 09:10:29 +00:00
From 658700d6424e852917b62c190dd23cbb3026b67d Mon Sep 17 00:00:00 2001
From: Iker Pedrosa <ipedrosa@redhat.com>
Date: Mon, 5 Aug 2024 15:15:44 +0200
Subject: [PATCH 08/40] pam_userdb: migrate backend database
pam_userdb module changed its backend database technology from lidb to
gdbm for RHEL10. This requires a set of leapp actors to perform the
database migration automatically when upgrading to RHEL10:
* ScanPamUserDB takes care of scanning the PAM service folder to detect
whether pam_userdb is used and the location of the database in use.
This information is stored in a model.
* CheckPamUserDB checks the databases reported by ScanPamUserDB and
prints a report about them.
* ConvertPamUserDB checks the databases reported by ScanPamUserDB and
converts them to GDBM format.
* RemoveOldPamUserDB checks the databases reported by ScanPamUserDB and
removes them.
All these actors include unit-tests.
Finally, there's also a spec file change to add `libdb-utils` dependency
as it is required to convert pam_userdb databases from BerkeleyDB to
GDBM.
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
---
packaging/leapp-repository.spec | 6 +++
.../actors/pamuserdb/checkpamuserdb/actor.py | 18 ++++++++
.../libraries/checkpamuserdb.py | 28 ++++++++++++
.../tests/test_checkpamuserdb.py | 43 +++++++++++++++++++
.../pamuserdb/convertpamuserdb/actor.py | 18 ++++++++
.../libraries/convertpamuserdb.py | 27 ++++++++++++
.../tests/test_convertpamuserdb.py | 39 +++++++++++++++++
.../pamuserdb/removeoldpamuserdb/actor.py | 18 ++++++++
.../libraries/removeoldpamuserdb.py | 25 +++++++++++
.../tests/test_removeoldpamuserdb.py | 38 ++++++++++++++++
.../actors/pamuserdb/scanpamuserdb/actor.py | 18 ++++++++
.../scanpamuserdb/libraries/scanpamuserdb.py | 29 +++++++++++++
.../tests/files/pam_userdb_basic | 1 +
.../tests/files/pam_userdb_complete | 9 ++++
.../tests/files/pam_userdb_missing | 1 +
.../scanpamuserdb/tests/test_scanpamuserdb.py | 27 ++++++++++++
.../el9toel10/models/pamuserdblocation.py | 14 ++++++
17 files changed, 359 insertions(+)
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/actor.py
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/libraries/checkpamuserdb.py
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/tests/test_checkpamuserdb.py
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/convertpamuserdb/actor.py
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/convertpamuserdb/libraries/convertpamuserdb.py
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/convertpamuserdb/tests/test_convertpamuserdb.py
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/removeoldpamuserdb/actor.py
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/removeoldpamuserdb/libraries/removeoldpamuserdb.py
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/removeoldpamuserdb/tests/test_removeoldpamuserdb.py
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/actor.py
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/libraries/scanpamuserdb.py
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/files/pam_userdb_basic
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/files/pam_userdb_complete
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/files/pam_userdb_missing
create mode 100644 repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/test_scanpamuserdb.py
create mode 100644 repos/system_upgrade/el9toel10/models/pamuserdblocation.py
diff --git a/packaging/leapp-repository.spec b/packaging/leapp-repository.spec
index 146afc45..0d63ba02 100644
--- a/packaging/leapp-repository.spec
+++ b/packaging/leapp-repository.spec
@@ -211,6 +211,12 @@ Requires: dracut
Requires: NetworkManager-libnm
Requires: python3-gobject-base
+%endif
+
+%if 0%{?rhel} && 0%{?rhel} == 9
+############# RHEL 9 dependencies (when the source system is RHEL 9) ##########
+# Required to convert pam_userdb database from BerkeleyDB to GDBM
+Requires: libdb-utils
%endif
##################################################
# end requirement
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/actor.py b/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/actor.py
new file mode 100644
index 00000000..8fada645
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/actor.py
@@ -0,0 +1,18 @@
+from leapp.actors import Actor
+from leapp.libraries.actor import checkpamuserdb
+from leapp.models import PamUserDbLocation, Report
+from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
+
+
+class CheckPamUserDb(Actor):
+ """
+ Create report with the location of pam_userdb databases
+ """
+
+ name = 'check_pam_user_db'
+ consumes = (PamUserDbLocation,)
+ produces = (Report,)
+ tags = (ChecksPhaseTag, IPUWorkflowTag)
+
+ def process(self):
+ checkpamuserdb.process()
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/libraries/checkpamuserdb.py b/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/libraries/checkpamuserdb.py
new file mode 100644
index 00000000..05cc71a9
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/libraries/checkpamuserdb.py
@@ -0,0 +1,28 @@
+from leapp import reporting
+from leapp.exceptions import StopActorExecutionError
+from leapp.libraries.stdlib import api
+from leapp.models import PamUserDbLocation
+
+FMT_LIST_SEPARATOR = "\n - "
+
+
+def process():
+ msg = next(api.consume(PamUserDbLocation), None)
+ if not msg:
+ raise StopActorExecutionError('Expected PamUserDbLocation, but got None')
+
+ if msg.locations:
+ reporting.create_report([
+ reporting.Title('pam_userdb databases will be converted to GDBM'),
+ reporting.Summary(
+ 'On RHEL 10, GDMB is used by pam_userdb as it\'s backend database,'
+ ' replacing BerkeleyDB. Existing pam_userdb databases will be'
+ ' converted to GDBM. The following databases will be converted:'
+ '{sep}{locations}'.format(sep=FMT_LIST_SEPARATOR, locations=FMT_LIST_SEPARATOR.join(msg.locations))),
+ reporting.Severity(reporting.Severity.INFO),
+ reporting.Groups([reporting.Groups.SECURITY, reporting.Groups.AUTHENTICATION])
+ ])
+ else:
+ api.current_logger().debug(
+ 'No pam_userdb databases were located, thus nothing will be converted'
+ )
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/tests/test_checkpamuserdb.py b/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/tests/test_checkpamuserdb.py
new file mode 100644
index 00000000..2e11106b
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/tests/test_checkpamuserdb.py
@@ -0,0 +1,43 @@
+import pytest
+
+from leapp import reporting
+from leapp.exceptions import StopActorExecutionError
+from leapp.libraries.actor import checkpamuserdb
+from leapp.libraries.common.testutils import create_report_mocked, logger_mocked
+from leapp.libraries.stdlib import api
+from leapp.models import PamUserDbLocation
+
+
+def test_process_no_msg(monkeypatch):
+ def consume_mocked(*args, **kwargs):
+ yield None
+
+ monkeypatch.setattr(api, 'consume', consume_mocked)
+
+ with pytest.raises(StopActorExecutionError):
+ checkpamuserdb.process()
+
+
+def test_process_no_location(monkeypatch):
+ def consume_mocked(*args, **kwargs):
+ yield PamUserDbLocation(locations=[])
+
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
+ monkeypatch.setattr(api, 'consume', consume_mocked)
+
+ checkpamuserdb.process()
+ assert (
+ 'No pam_userdb databases were located, thus nothing will be converted'
+ in api.current_logger.dbgmsg
+ )
+
+
+def test_process_locations(monkeypatch):
+ def consume_mocked(*args, **kwargs):
+ yield PamUserDbLocation(locations=['/tmp/db1', '/tmp/db2'])
+
+ monkeypatch.setattr(reporting, "create_report", create_report_mocked())
+ monkeypatch.setattr(api, 'consume', consume_mocked)
+
+ checkpamuserdb.process()
+ assert reporting.create_report.called == 1
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/convertpamuserdb/actor.py b/repos/system_upgrade/el9toel10/actors/pamuserdb/convertpamuserdb/actor.py
new file mode 100644
index 00000000..5f8525b6
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/convertpamuserdb/actor.py
@@ -0,0 +1,18 @@
+from leapp.actors import Actor
+from leapp.libraries.actor import convertpamuserdb
+from leapp.models import PamUserDbLocation
+from leapp.tags import IPUWorkflowTag, PreparationPhaseTag
+
+
+class ConvertPamUserDb(Actor):
+ """
+ Convert the pam_userdb databases to GDBM
+ """
+
+ name = 'convert_pam_user_db'
+ consumes = (PamUserDbLocation,)
+ produces = ()
+ tags = (PreparationPhaseTag, IPUWorkflowTag)
+
+ def process(self):
+ convertpamuserdb.process()
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/convertpamuserdb/libraries/convertpamuserdb.py b/repos/system_upgrade/el9toel10/actors/pamuserdb/convertpamuserdb/libraries/convertpamuserdb.py
new file mode 100644
index 00000000..e55b4102
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/convertpamuserdb/libraries/convertpamuserdb.py
@@ -0,0 +1,27 @@
+from leapp.exceptions import StopActorExecutionError
+from leapp.libraries.stdlib import api, CalledProcessError, run
+from leapp.models import PamUserDbLocation
+
+
+def _convert_db(db_path):
+ cmd = ['db_converter', '--src', f'{db_path}.db', '--dest', f'{db_path}.gdbm']
+ try:
+ run(cmd)
+ except (CalledProcessError, OSError) as e:
+ # As the db_converter does not remove the original DB after conversion or upon failure,
+ # interrupt the upgrade, keeping the original DBs.
+ # If all DBs are successfully converted, the leftover DBs are removed in the removeoldpamuserdb actor.
+ raise StopActorExecutionError(
+ 'Cannot convert pam_userdb database.',
+ details={'details': '{}: {}'.format(str(e), e.stderr)}
+ )
+
+
+def process():
+ msg = next(api.consume(PamUserDbLocation), None)
+ if not msg:
+ raise StopActorExecutionError('Expected PamUserDbLocation, but got None')
+
+ if msg.locations:
+ for location in msg.locations:
+ _convert_db(location)
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/convertpamuserdb/tests/test_convertpamuserdb.py b/repos/system_upgrade/el9toel10/actors/pamuserdb/convertpamuserdb/tests/test_convertpamuserdb.py
new file mode 100644
index 00000000..46505492
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/convertpamuserdb/tests/test_convertpamuserdb.py
@@ -0,0 +1,39 @@
+import os
+
+import pytest
+
+from leapp.exceptions import StopActorExecutionError
+from leapp.libraries.actor import convertpamuserdb
+from leapp.libraries.common.testutils import logger_mocked
+from leapp.libraries.stdlib import api, CalledProcessError
+
+CUR_DIR = os.path.dirname(os.path.abspath(__file__))
+
+
+def test_convert_db_success(monkeypatch):
+ location = os.path.join(CUR_DIR, '/files/db1')
+
+ def run_mocked(cmd, **kwargs):
+ assert cmd == ['db_converter', '--src', f'{location}.db', '--dest', f'{location}.gdbm']
+
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
+ monkeypatch.setattr(convertpamuserdb, 'run', run_mocked)
+ convertpamuserdb._convert_db(location)
+ assert len(api.current_logger.errmsg) == 0
+
+
+def test_convert_db_failure(monkeypatch):
+ location = os.path.join(CUR_DIR, '/files/db1')
+
+ def run_mocked(cmd, **kwargs):
+ raise CalledProcessError(
+ message='A Leapp Command Error occurred.',
+ command=cmd,
+ result={'exit_code': 1}
+ )
+
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
+ monkeypatch.setattr(convertpamuserdb, 'run', run_mocked)
+ with pytest.raises(StopActorExecutionError) as err:
+ convertpamuserdb._convert_db(location)
+ assert str(err.value) == 'Cannot convert pam_userdb database.'
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/removeoldpamuserdb/actor.py b/repos/system_upgrade/el9toel10/actors/pamuserdb/removeoldpamuserdb/actor.py
new file mode 100644
index 00000000..39a00855
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/removeoldpamuserdb/actor.py
@@ -0,0 +1,18 @@
+from leapp.actors import Actor
+from leapp.libraries.actor import removeoldpamuserdb
+from leapp.models import PamUserDbLocation
+from leapp.tags import ApplicationsPhaseTag, IPUWorkflowTag
+
+
+class RemoveOldPamUserDb(Actor):
+ """
+ Remove old pam_userdb databases
+ """
+
+ name = 'remove_old_pam_user_db'
+ consumes = (PamUserDbLocation,)
+ produces = ()
+ tags = (ApplicationsPhaseTag, IPUWorkflowTag)
+
+ def process(self):
+ removeoldpamuserdb.process()
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/removeoldpamuserdb/libraries/removeoldpamuserdb.py b/repos/system_upgrade/el9toel10/actors/pamuserdb/removeoldpamuserdb/libraries/removeoldpamuserdb.py
new file mode 100644
index 00000000..5fc4cb4d
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/removeoldpamuserdb/libraries/removeoldpamuserdb.py
@@ -0,0 +1,25 @@
+from leapp.exceptions import StopActorExecutionError
+from leapp.libraries.stdlib import api, CalledProcessError, run
+from leapp.models import PamUserDbLocation
+
+
+def _remove_db(db_path):
+ cmd = ['rm', '-f', f'{db_path}.db']
+ try:
+ run(cmd)
+ except (CalledProcessError, OSError) as e:
+ api.current_logger().error(
+ 'Failed to remove {}.db: {}'.format(
+ db_path, e
+ )
+ )
+
+
+def process():
+ msg = next(api.consume(PamUserDbLocation), None)
+ if not msg:
+ raise StopActorExecutionError('Expected PamUserDbLocation, but got None')
+
+ if msg.locations:
+ for location in msg.locations:
+ _remove_db(location)
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/removeoldpamuserdb/tests/test_removeoldpamuserdb.py b/repos/system_upgrade/el9toel10/actors/pamuserdb/removeoldpamuserdb/tests/test_removeoldpamuserdb.py
new file mode 100644
index 00000000..2c1d5c75
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/removeoldpamuserdb/tests/test_removeoldpamuserdb.py
@@ -0,0 +1,38 @@
+import os
+
+from leapp.libraries.actor import removeoldpamuserdb
+from leapp.libraries.common.testutils import logger_mocked
+from leapp.libraries.stdlib import api, CalledProcessError
+
+CUR_DIR = os.path.dirname(os.path.abspath(__file__))
+
+
+def test_remove_db_success(monkeypatch):
+ location = os.path.join(CUR_DIR, '/files/db1')
+
+ def run_mocked(cmd, **kwargs):
+ assert cmd == ['rm', '-f', f'{location}.db']
+
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
+ monkeypatch.setattr(removeoldpamuserdb, 'run', run_mocked)
+ removeoldpamuserdb._remove_db(location)
+ assert len(api.current_logger.errmsg) == 0
+
+
+def test_remove_db_failure(monkeypatch):
+ location = os.path.join(CUR_DIR, '/files/db1')
+
+ def run_mocked(cmd, **kwargs):
+ raise CalledProcessError(
+ message='A Leapp Command Error occurred.',
+ command=cmd,
+ result={'exit_code': 1}
+ )
+
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
+ monkeypatch.setattr(removeoldpamuserdb, 'run', run_mocked)
+ removeoldpamuserdb._remove_db(location)
+ assert (
+ 'Failed to remove /files/db1.db'
+ not in api.current_logger.errmsg
+ )
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/actor.py b/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/actor.py
new file mode 100644
index 00000000..b6b35f1a
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/actor.py
@@ -0,0 +1,18 @@
+from leapp.actors import Actor
+from leapp.libraries.actor import scanpamuserdb
+from leapp.models import PamUserDbLocation
+from leapp.tags import FactsPhaseTag, IPUWorkflowTag
+
+
+class ScanPamUserDb(Actor):
+ """
+ Scan the PAM service folder for the location of pam_userdb databases
+ """
+
+ name = 'scan_pam_user_db'
+ consumes = ()
+ produces = (PamUserDbLocation,)
+ tags = (FactsPhaseTag, IPUWorkflowTag)
+
+ def process(self):
+ self.produce(scanpamuserdb.parse_pam_config_folder('/etc/pam.d/'))
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/libraries/scanpamuserdb.py b/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/libraries/scanpamuserdb.py
new file mode 100644
index 00000000..0f668c02
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/libraries/scanpamuserdb.py
@@ -0,0 +1,29 @@
+import os
+import re
+
+from leapp.models import PamUserDbLocation
+
+
+def _parse_pam_config_file(conf_file):
+ with open(conf_file, 'r') as file:
+ for line in file:
+ if 'pam_userdb' in line:
+ match = re.search(r'db=(\S+)', line)
+ if match:
+ return match.group(1)
+
+ return None
+
+
+def parse_pam_config_folder(conf_folder):
+ locations = set()
+
+ for file_name in os.listdir(conf_folder):
+ file_path = os.path.join(conf_folder, file_name)
+
+ if os.path.isfile(file_path):
+ location = _parse_pam_config_file(file_path)
+ if location is not None:
+ locations.add(location)
+
+ return PamUserDbLocation(locations=list(locations))
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/files/pam_userdb_basic b/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/files/pam_userdb_basic
new file mode 100644
index 00000000..f115147b
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/files/pam_userdb_basic
@@ -0,0 +1 @@
+auth required pam_userdb.so db=/tmp/db1
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/files/pam_userdb_complete b/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/files/pam_userdb_complete
new file mode 100644
index 00000000..84e40b48
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/files/pam_userdb_complete
@@ -0,0 +1,9 @@
+auth required pam_env.so
+auth required pam_faildelay.so delay=2000000
+auth sufficient pam_fprintd.so
+auth [default=1 ignore=ignore success=ok] pam_usertype.so isregular
+auth [default=1 ignore=ignore success=ok] pam_localuser.so
+auth required pam_userdb.so db=/tmp/db2
+auth [default=1 ignore=ignore success=ok] pam_usertype.so isregular
+auth sufficient pam_sss.so forward_pass
+auth required pam_deny.so
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/files/pam_userdb_missing b/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/files/pam_userdb_missing
new file mode 100644
index 00000000..764947fc
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/files/pam_userdb_missing
@@ -0,0 +1 @@
+auth sufficient pam_unix.so nullok
diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/test_scanpamuserdb.py b/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/test_scanpamuserdb.py
new file mode 100644
index 00000000..3b752d87
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/scanpamuserdb/tests/test_scanpamuserdb.py
@@ -0,0 +1,27 @@
+import os
+
+import pytest
+
+from leapp.libraries.actor import scanpamuserdb
+
+CUR_DIR = os.path.dirname(os.path.abspath(__file__))
+
+
+@pytest.mark.parametrize(
+ "inp,exp_out",
+ [
+ ("files/pam_userdb_missing", None),
+ ("files/pam_userdb_basic", "/tmp/db1"),
+ ("files/pam_userdb_complete", "/tmp/db2"),
+ ],
+)
+def test_parse_pam_config_file(inp, exp_out):
+ file = scanpamuserdb._parse_pam_config_file(os.path.join(CUR_DIR, inp))
+ assert file == exp_out
+
+
+def test_parse_pam_config_folder():
+ msg = scanpamuserdb.parse_pam_config_folder(os.path.join(CUR_DIR, "files/"))
+ assert len(msg.locations) == 2
+ assert "/tmp/db1" in msg.locations
+ assert "/tmp/db2" in msg.locations
diff --git a/repos/system_upgrade/el9toel10/models/pamuserdblocation.py b/repos/system_upgrade/el9toel10/models/pamuserdblocation.py
new file mode 100644
index 00000000..d15b2041
--- /dev/null
+++ b/repos/system_upgrade/el9toel10/models/pamuserdblocation.py
@@ -0,0 +1,14 @@
+from leapp.models import fields, Model
+from leapp.topics import SystemInfoTopic
+
+
+class PamUserDbLocation(Model):
+ """
+ Provides a list of all database files for pam_userdb
+ """
+ topic = SystemInfoTopic
+
+ locations = fields.List(fields.String(), default=[])
+ """
+ The list with the full path to the database files.
+ """
--
2.47.0