diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bea01c5..e73d3a9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,7 +3,7 @@ image: fedora:rawhide variables: DEPENDENCIES: dbus-glib-devel pam-devel polkit-devel gtk-doc meson intltool autoconf automake libtool - gcc gcc-c++ glibc-devel make python3-dbusmock + gcc gcc-c++ glibc-devel make python3-dbusmock python3-libpamtest DEPENDENCIES_STABLE: $DEPENDENCIES libfprint-devel DEPENDENCIES_DEV: $DEPENDENCIES git # Sync'ed up with https://gitlab.freedesktop.org/libfprint/libfprint/blob/master/.gitlab-ci.yml diff --git a/configure.ac b/configure.ac index 6e8c5c6..018df14 100644 --- a/configure.ac +++ b/configure.ac @@ -91,5 +91,7 @@ doc/Makefile doc/version.xml doc/dbus/Makefile tests/Makefile +tests/pam/Makefile +tests/pam/services/Makefile po/Makefile.in ]) diff --git a/tests/Makefile.am b/tests/Makefile.am index 8280236..3c9f3f5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,3 +1,5 @@ +SUBDIRS = pam + TESTS_ENVIRONMENT = export FPRINT_BUILD_DIR=$(abs_top_builddir)/src; export TOPSRCDIR=$(abs_top_srcdir); export PYTHON=@PYTHON@; TESTS = fprintd.py test_fprintd_utils.py diff --git a/tests/pam/Makefile.am b/tests/pam/Makefile.am new file mode 100644 index 0000000..818192e --- /dev/null +++ b/tests/pam/Makefile.am @@ -0,0 +1,6 @@ +SUBDIRS = services + +TESTS_ENVIRONMENT = export TOPSRCDIR=$(abs_top_srcdir); export LD_PRELOAD=libpam_wrapper.so; export PAM_WRAPPER_SERVICE_DIR=$(abs_top_builddir)/tests/pam/services; export PAM_WRAPPER=1; export PYTHON=@PYTHON@; +TESTS = test_pam_fprintd.py + +EXTRA_DIST = $(TESTS) diff --git a/tests/pam/services/Makefile.am b/tests/pam/services/Makefile.am new file mode 100644 index 0000000..e53c53b --- /dev/null +++ b/tests/pam/services/Makefile.am @@ -0,0 +1,7 @@ +all-am: fprintd-pam-test + +fprintd-pam-test: fprintd-pam-test.in Makefile + sed -e "s|\@TOPBUILDDIR\@|$(abs_top_builddir)|" $< > $@ + +EXTRA_DIST = fprintd-pam-test.in +CLEANFILES = fprintd-pam-test diff --git a/tests/pam/services/fprintd-pam-test.in b/tests/pam/services/fprintd-pam-test.in new file mode 100644 index 0000000..3a64a6b --- /dev/null +++ b/tests/pam/services/fprintd-pam-test.in @@ -0,0 +1 @@ +auth required @TOPBUILDDIR@/pam/.libs/pam_fprintd.so debug diff --git a/tests/pam/test_pam_fprintd.py b/tests/pam/test_pam_fprintd.py new file mode 100755 index 0000000..784b22c --- /dev/null +++ b/tests/pam/test_pam_fprintd.py @@ -0,0 +1,100 @@ +#!/usr/bin/python3 + +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 3 of the License, or (at your option) any +# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text +# of the license. + +__author__ = 'Bastien Nocera' +__email__ = 'hadess@hadess.net' +__copyright__ = '(c) 2020 Red Hat Inc.' +__license__ = 'LGPL 3+' + +import tempfile +import unittest +import sys +import subprocess +import dbus +import dbus.mainloop.glib +import dbusmock +import fcntl +import os +import time +import pypamtest + +dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + +PAM_SUCCESS = 0 +PAM_AUTH_ERR = 7 +PAM_AUTHINFO_UNAVAIL = 9 +PAM_USER_UNKNOWN = 10 + +class TestPamFprintd(dbusmock.DBusTestCase): + '''Test pam_fprintd''' + + @classmethod + def setUpClass(klass): + klass.start_system_bus() + klass.dbus_con = klass.get_dbus(True) + + template_path = './' + if 'TOPSRCDIR' in os.environ: + template_path = os.environ['TOPSRCDIR'] + '/tests/' + klass.template_name = template_path + 'dbusmock/fprintd.py' + print ('Using template from %s' % klass.template_name) + + def setUp(self): + (self.p_mock, self.obj_fprintd_manager) = self.spawn_server_template( + self.template_name, {}, stdout=subprocess.PIPE) + # set log to nonblocking + flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL) + fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) + self.obj_fprintd_mock = dbus.Interface(self.obj_fprintd_manager, 'net.reactivated.Fprint.Manager.Mock') + + def tearDown(self): + self.p_mock.terminate() + self.p_mock.wait() + + def setup_device(self): + device_path = self.obj_fprintd_mock.AddDevice('FDO Trigger Finger Laser Reader', 3, 'swipe') + self.device_mock = self.dbus_con.get_object('net.reactivated.Fprint', device_path) + self.device_mock.SetEnrolledFingers('toto', ['left-little-finger', 'right-little-finger']) + + def test_pam_fprintd_auth(self): + self.setup_device() + script = [ + ( 'verify-match', True, 2 ) + ] + self.device_mock.SetVerifyScript(script) + + tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_SUCCESS) + res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ]) + + self.assertRegex(res.info[0], r'Swipe your left little finger across the fingerprint reader') + self.assertEqual(len(res.errors), 0) + + def test_pam_fprintd_failed_auth(self): + self.setup_device() + script = [ + ( 'verify-no-match', True, 1 ), + ( 'verify-no-match', True, 1 ), + ( 'verify-no-match', True, 1 ), + ] + self.device_mock.SetVerifyScript(script) + + tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTH_ERR) + res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ]) + + self.assertRegex(res.info[0], r'Swipe your left little finger across the fingerprint reader') + self.assertEqual(len(res.errors), 3) + self.assertRegex(res.errors[0], r'Failed to match fingerprint') + self.assertRegex(res.errors[1], r'Failed to match fingerprint') + self.assertRegex(res.errors[2], r'Failed to match fingerprint') + +if __name__ == '__main__': + if 'PAM_WRAPPER_SERVICE_DIR' not in os.environ: + print('Cannot run test without environment set correctly, run "make check" instead') + sys.exit(1) + # set stream to sys.stderr to get debug output + unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))