diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 974a25f..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,132 +0,0 @@ -include: - - project: 'libfprint/libfprint' - ref: master - file: '/.gitlab-ci/libfprint-templates.yaml' - - project: 'freedesktop/ci-templates' - ref: master - file: '/templates/fedora.yml' - -variables: - extends: .libfprint_common_variables - FDO_DISTRIBUTION_TAG: latest - FDO_DISTRIBUTION_VERSION: rawhide - FDO_UPSTREAM_REPO: "libfprint/$CI_PROJECT_NAME" - FEDORA_IMAGE: "$CI_REGISTRY/libfprint/$CI_PROJECT_NAME/fedora/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG" - DEPENDENCIES: dbus-glib-devel - gcc - gcovr - gettext - git - glibc-devel - gtk-doc - libasan - libfprint-devel - meson - patch - pam-devel - polkit-devel - python3-dbusmock - python3-libpamtest - systemd-devel - -image: "$FEDORA_IMAGE" - -stages: - - check-source - - build - - test - -.fprintd_build_preconditions: - except: - variables: - - $FPRINT_CRON_TASK == "BUILD_CI_IMAGES" - -.install_libfprint_dev: - before_script: - # Make sure we don't build or link against the system libfprint - - dnf remove -y libfprint-devel - - git clone https://gitlab.freedesktop.org/libfprint/libfprint.git - - cd libfprint - - meson . _build --prefix=/usr -Ddrivers=virtual_image,virtual_device,virtual_device_storage -Ddoc=false - - ninja -C _build - - ninja -C _build install - - cd .. - # So we don't get error about this libfprint file - - echo "libfprint/demo/gtk-libfprint-test.ui" >> po/POTFILES.skip - -test_indent: - stage: check-source - extends: .fprintd_build_preconditions - script: - - scripts/uncrustify.sh - - git diff - - "! git status -s | grep -q ." - -build_stable: - extends: .fprintd_build_preconditions - stage: build - allow_failure: true - script: - - meson _build - - ninja -C _build -v - - ninja -C _build -v install - -build_dev: - extends: - - .fprintd_build_preconditions - - .install_libfprint_dev - stage: build - script: - - meson _build --werror -Dgtk_doc=true - - ninja -C _build -v - - ninja -C _build -v install - artifacts: - name: log - when: on_failure - paths: - - _build/meson-logs/*.txt - -test_dev: - extends: - - .fprintd_build_preconditions - - .install_libfprint_dev - stage: test - script: - - meson _build -Db_coverage=true - - meson test -C _build --print-errorlogs --no-stdsplit --timeout-multiplier 3 - - ninja -C _build coverage - - cat _build/meson-logs/coverage.txt - artifacts: - expose_as: 'Coverage Report' - paths: - - _build/meson-logs - - _build/meson-logs/coveragereport/index.html - expire_in: 1 week - -test_dev_with_sanitizer: - extends: - - .fprintd_build_preconditions - - .install_libfprint_dev - stage: test - script: - - meson _build -Db_sanitize=address - - meson test -C _build --print-errorlogs --no-stdsplit --timeout-multiplier 5 - artifacts: - name: meson-logs - when: on_failure - paths: - - _build/meson-logs - -# CONTAINERS creation stage -container_fedora_build: - extends: .fdo.container-build@fedora - only: - variables: - - $FPRINT_CRON_TASK == "BUILD_CI_IMAGES" - variables: - GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image - FDO_FORCE_REBUILD: 1 - # a list of packages to install - FDO_DISTRIBUTION_PACKAGES: - $DEPENDENCIES - $LIBFPRINT_DEPENDENCIES diff --git a/NEWS b/NEWS deleted file mode 100644 index e0f908b..0000000 --- a/NEWS +++ /dev/null @@ -1,195 +0,0 @@ -This file lists notable changes in each release. For the full history of all -changes, see ChangeLog. - -Version 1.94.1: - -API users, please adjust now to planned API changes in 2.0: - - EnrollStart will throw an error if the finger was enrolled already - - Interactive DBus authentication will only happen when requested - -Highlights. - - Fix systemd unit so that udev hotplug events are processed - - Report back the selected finger if there is only one - - Change PolicyKit strings for clarity - - Various fixes to the testsuite - - Plenty of translation updates - -Version 1.94.0: - -API users, please adjust now to planned API changes in 2.0: - - EnrollStart will throw an error if the finger was enrolled already - - Interactive DBus authentication will only happen when requested - -Highlights: - - Implement suspend/resume handling. - This requires writing "power/persist" and "power/wakeup" in sysfs. - - Support libfprint overheat protections - - Delete host prints when device prints disappeared - - pam: Immediately return success information - - Plenty of updated translations thanks to move to Fedora Weblate - - Fix possible race when retrieving session information - - Fix possible race when a client disconnects - - GLib 2.56 compatibility fixes - - -Version 1.92.0: - -API users, please adjust now to planned API changes in 2.0: - - EnrollStart will throw an error if the finger was enrolled already - - Interactive DBus authentication will only happen when requested - -Highlights: - - fprintd now prevents the same finger to be enrolled twice - - Support clearing storage of match-on-chip devices - - pam: Cancel authentication on SIGINT (e.g. ctrl+c with sudo) - - pam: Always return PAM_AUTHINFO_UNAVAIL for devices without prints - - Expose finger status on DBus - - Add method to delete only a specific print of a user - - Improved error reporting for deletion - - Wait for finger removal before cancelling operations - - Prefer older prints when garbage collecting - - Major improvements to test coverage - -Version 1.90.9: - -Highlights: - - Fix multiple daemon lockup issues (#97) - - Fix print garbage collection to not delete used prints - - pam: Use the device with the most prints - - -Version 1.90.8: - -It seems that we are finally reaching the end of the tunnel with regard -to regressions. One more issue that cropped up was that a pam_fprintd fix -to avoid a possible authentication bypass caused issues when fprintd was -just started on demand. - -Highlights: - - pam: Only listen to NameOwnerChanged after fprintd is known to run (#94) - - Place new ObjectManager DBus API at /net/reactivated/Fprint - - -Version 1.90.7: - -While 1.90.6 fixed a number of issues, we did have a bad regression due -causing pam_fprintd to crash when there are no fingerprint devices -installed. - -Highlights: - - pam: Guard strdup calls against NULL pointers - - -Version 1.90.6: - -The 1.90.5 release was unusable due to a number of inter-related issues -with the DBus interface and authorization. We also found a number of -problems with possible security implications. - -Currently fprintd will do interactive authorization even if this was not -requested using the correct DBus method call flag. All API users MUST be -updated to set the flag as it will be enabled in the future! - -Highlights: - - Fix fprintd DBus configuration - - Change details of what requires authorization - - Fix various race conditions in pam_fprintd - - Permit interactive authorization from fprintd utilities - - Do not allow deletion while another operation is ongoing - - -Version 1.90.5: - -The 1.90.4 release contained some bad errors, this release addresses those. - -Highlights: - - Permit building with polkit older than 0.114 - - Fix possible issues with PAM test - - Fix incorrect DBus policy - - Fix build so that CFLAGS enviroment is correctly used - - Skip hotplug test with older libfprint (which times out otherwise) - -Version 1.90.4: - -This fprintd release contains major core reworkings and improved testing. -As such, only the most important changes are listed here, focusing on -changes relevant to distributors. - -Highlights: - - Authentication is now required to enroll a new print (#5) - - Add support for the libfprint early reporting mechanism - - Proper hotplug support together with libfprint 1.90.4 - - Handle STATE_DIRECTORY containing multiple paths - -version 1.90.1: -- Add support for prints saved on the fingerprint device itself -- Add integration tests using the virtual image driver, and further - tests for the utilities -- Port build system to meson -- Loads of build warnings and memory leak fixes - -- PAM module: - - Port PAM module to sd-bus from dbus-glib - - Use systemd to not ask for a fingerprint scan on remote logins - - Add man page for PAM module - - Add tests - -This version requires libfprint 1.90.1, a 2.0 pre-release. - -version 0.9.0: -- Fix hangs when there the verification error was "retry" -- Update for fp_get_pollfds() changes -- Fix "client_username" memory leak, fix memory leak when saving a file -- Create the fingerprint storage directory at install time, - the storage path is now hard-coded as /var/lib/fprint and created by - systemd when the service is started - -version 0.8.1: -- Fix build when builddir != srcdir -- Fix possible crash on exit -- Avoid warnings in copy/paste header -- Sandbox fprintd daemon more -- Update website address -- Minimise debug output -- Updated translations - -version 0.8.0: -- Lockdown the daemon to minimise potential security issues -- Don't wake up readers when there's no enrolled fingerprints - -version 0.7.0: -- Fix crash in the daemon when cancelling PAM conversation -- Fix build warnings and update translations - -version 0.6.0: -- Fix warning in fprintd.pod file -- Reduce logging during normal operation -- Fix eventfd leak in PAM module -- List possible values for finger when enrolling -- Fix possible crash in fprintd-verify -- Fix listing and deleting fingerprints when there's more than - one reader available - -version 0.5.1: -- Add max-tries and timeout arguments to PAM module -- Add ability to require the fingerprint for enrolled users -- Add "-f " option to enroll utilities - -version 0.5.0: -- Don't use a device name if there's only one reader -- Avoid possible crash when trying to login without devices -- Fix possible crashes due to uninitialised variables -- Fix hang when the machine has no USB bus -- Add a systemd unit file - -version 0.4.1: -- Enable gtk-doc by default so that file aren't - missing in the tarball - -version 0.4.0: -- Loads of new translations -- Fix crasher when PAM module gets unloaded -- Use GIO to monitor D-Bus clients instead of custom code - -version 0.2.0: -- First actual release diff --git a/TODO b/TODO deleted file mode 100644 index 9fac396..0000000 --- a/TODO +++ /dev/null @@ -1,6 +0,0 @@ -Verify PAM messages fit with GDM/gnome-screensaver - -Automatically show the fingerprint registration when logged in and -not having any registered prints? -http://uk.youtube.com/watch?v=F_x_vwCltbc - diff --git a/code-of-conduct.md b/code-of-conduct.md deleted file mode 100644 index a429a42..0000000 --- a/code-of-conduct.md +++ /dev/null @@ -1,3 +0,0 @@ -This project and its community follow the [Freedesktop.org code of conduct] - -[Freedesktop.org code of conduct]: https://www.freedesktop.org/wiki/CodeOfConduct/ diff --git a/data/fprintd.1 b/data/fprintd.1 deleted file mode 100644 index 0636df7..0000000 --- a/data/fprintd.1 +++ /dev/null @@ -1,213 +0,0 @@ -.\" Automatically generated by Pod::Man 4.12 (Pod::Simple 3.39) -.\" -.\" Standard preamble: -.\" ======================================================================== -.de Sp \" Vertical space (when we can't use .PP) -.if t .sp .5v -.if n .sp -.. -.de Vb \" Begin verbatim text -.ft CW -.nf -.ne \\$1 -.. -.de Ve \" End verbatim text -.ft R -.fi -.. -.\" Set up some character translations and predefined strings. \*(-- will -.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left -.\" double quote, and \*(R" will give a right double quote. \*(C+ will -.\" give a nicer C++. Capital omega is used to do unbreakable dashes and -.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, -.\" nothing in troff, for use with C<>. -.tr \(*W- -.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' -.ie n \{\ -. ds -- \(*W- -. ds PI pi -. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch -. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch -. ds L" "" -. ds R" "" -. ds C` -. ds C' -'br\} -.el\{\ -. ds -- \|\(em\| -. ds PI \(*p -. ds L" `` -. ds R" '' -. ds C` -. ds C' -'br\} -.\" -.\" Escape single quotes in literal strings from groff's Unicode transform. -.ie \n(.g .ds Aq \(aq -.el .ds Aq ' -.\" -.\" If the F register is >0, we'll generate index entries on stderr for -.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index -.\" entries marked with X<> in POD. Of course, you'll have to process the -.\" output yourself in some meaningful fashion. -.\" -.\" Avoid warning from groff about undefined register 'F'. -.de IX -.. -.nr rF 0 -.if \n(.g .if rF .nr rF 1 -.if (\n(rF:(\n(.g==0)) \{\ -. if \nF \{\ -. de IX -. tm Index:\\$1\t\\n%\t"\\$2" -.. -. if !\nF==2 \{\ -. nr % 0 -. nr F 2 -. \} -. \} -.\} -.rr rF -.\" -.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). -.\" Fear. Run. Save yourself. No user-serviceable parts. -. \" fudge factors for nroff and troff -.if n \{\ -. ds #H 0 -. ds #V .8m -. ds #F .3m -. ds #[ \f1 -. ds #] \fP -.\} -.if t \{\ -. ds #H ((1u-(\\\\n(.fu%2u))*.13m) -. ds #V .6m -. ds #F 0 -. ds #[ \& -. ds #] \& -.\} -. \" simple accents for nroff and troff -.if n \{\ -. ds ' \& -. ds ` \& -. ds ^ \& -. ds , \& -. ds ~ ~ -. ds / -.\} -.if t \{\ -. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" -. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' -. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' -. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' -. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' -. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' -.\} -. \" troff and (daisy-wheel) nroff accents -.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' -.ds 8 \h'\*(#H'\(*b\h'-\*(#H' -.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] -.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' -.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' -.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] -.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] -.ds ae a\h'-(\w'a'u*4/10)'e -.ds Ae A\h'-(\w'A'u*4/10)'E -. \" corrections for vroff -.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' -.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' -. \" for low resolution devices (crt and lpr) -.if \n(.H>23 .if \n(.V>19 \ -\{\ -. ds : e -. ds 8 ss -. ds o a -. ds d- d\h'-1'\(ga -. ds D- D\h'-1'\(hy -. ds th \o'bp' -. ds Th \o'LP' -. ds ae ae -. ds Ae AE -.\} -.rm #[ #] #H #V #F C -.\" ======================================================================== -.\" -.IX Title "fprintd 1" -.TH fprintd 1 "2020-01-24" "freedesktop" "" -.\" For nroff, turn off justification. Always turn off hyphenation; it makes -.\" way too many mistakes in technical documents. -.if n .ad l -.nh -.SH "NAME" -fprintd \- Fingerprint management daemon, and test applications -.SH "SYNOPSIS" -.IX Header "SYNOPSIS" -\&\fBfprintd-enroll\fR [\-f finger] [usename] -.PP -\&\fBfprintd-list\fR username [usernames...] -.PP -\&\fBfprintd-verify\fR [\-f finger] [usename] -.PP -\&\fBfprintd-delete\fR username [usernames...] -.SH "DESCRIPTION" -.IX Header "DESCRIPTION" -This manual page documents briefly the \fBfprintd\fR command-line utilities. -.PP -The \fBfprintd\fR daemon is accessed through \fBD\-Bus\fR by those command-line utilities. -.SH "ARGUMENTS" -.IX Header "ARGUMENTS" -.IP "\fBusername\fR" 8 -.IX Item "username" -The username for the user for which you want to query or modify the fingerprint database. -.Sp -Not that \fBfprintd-list\fR and \fBfprintd-delete\fR require at least one username to be passed, and support multiple usernames. -.Sp -\&\fBfprintd-enroll\fR and \fBfprintd-verify\fR will use the current username if none are passed on the command-line. -.IP "\fB\-f finger\fR" 8 -.IX Item "-f finger" -For \fBfprintd-enroll\fR, the finger to enroll. Possible values are: -.Sp -left-thumb, -left-index-finger, -left-middle-finger, -left-ring-finger, -left-little-finger, -right-thumb, -right-index-finger, -right-middle-finger, -right-ring-finger, -right-little-finger. -.Sp -The default is automatic, selecting the first available finger for swipe devices, or all the enrolled fingers, for press devices. -.SH "TEST APPLICATIONS" -.IX Header "TEST APPLICATIONS" -.SS "fprintd-enroll" -.IX Subsection "fprintd-enroll" -.RS 8 -Will enroll the user's right index finger into the database. -.RE -.SS "fprintd-list" -.IX Subsection "fprintd-list" -.RS 8 -Will list the user's enrolled fingerprints. -.RE -.SS "fprintd-verify" -.IX Subsection "fprintd-verify" -.RS 8 -Will verify the user's fingerprints against the database. -.RE -.SS "fprintd-delete" -.IX Subsection "fprintd-enroll" -.RS 8 -Will delete the user's right index finger into the database. -.RE -.SH "AUTHOR" -.IX Header "AUTHOR" -\&\fBfprintd\fR was written by Bastien Nocera and Daniel Drake. -.SH "DIRECTORIES" -.IX Header "DIRECTORIES" -By default, fprintd stores the fingerprints in \fB/var/lib/fprint/\fR -.SH "SEE ALSO" -.IX Header "SEE ALSO" -.IP "\fBgnome-control-center\fR" 8 -.IX Item "gnome-control-center" diff --git a/data/fprintd.conf b/data/fprintd.conf deleted file mode 100644 index 40b4b0c..0000000 --- a/data/fprintd.conf +++ /dev/null @@ -1,2 +0,0 @@ -[storage] -type=file diff --git a/data/fprintd.pod b/data/fprintd.pod deleted file mode 100644 index da8ab92..0000000 --- a/data/fprintd.pod +++ /dev/null @@ -1,104 +0,0 @@ -=head1 NAME - -fprintd - Fingerprint management daemon, and test applications - -=head1 SYNOPSIS - -B [-f finger] [usename] - -B username [usernames...] - -B [-f finger] [usename] - -B username [usernames...] - -=head1 DESCRIPTION - -This manual page documents briefly the B command-line utilities. - -The B daemon is accessed through B by those command-line utilities. - -=head1 ARGUMENTS - -=over 8 - -=item B - -The username for the user for which you want to query or modify the fingerprint database. - -Not that B and B require at least one username to be passed, and support multiple usernames. - -B and B will use the current username if none are passed on the command-line. - -=item B<-f finger> - -For B, the finger to enroll. Possible values are: - -left-thumb, -left-index-finger, -left-middle-finger, -left-ring-finger, -left-little-finger, -right-thumb, -right-index-finger, -right-middle-finger, -right-ring-finger, -right-little-finger. - -The default is automatic, selecting the first available finger for swipe devices, or all the enrolled fingers, for press devices. - -=back - -=head1 TEST APPLICATIONS - -=over 8 - -=back - -=head2 fprintd-enroll - -=over 8 - -Will enroll the user's right index finger into the database. - -=back - -=head2 fprintd-list - -=over 8 - -Will list the user's enrolled fingerprints. - -=back - -=head2 fprintd-verify - -=over 8 - -Will verify the user's fingerprints against the database. - -=back - -=head2 fprintd-delete - -=over 8 - -Will delete the user's right index finger into the database. - -=back - -=head1 AUTHOR - -B was written by Bastien Nocera and Daniel Drake. - -=head1 DIRECTORIES - -By default, fprintd stores the fingerprints in B - -=head1 SEE ALSO - -=over 8 - -=item B - -=back diff --git a/data/fprintd.service.in b/data/fprintd.service.in deleted file mode 100644 index 9ebdd88..0000000 --- a/data/fprintd.service.in +++ /dev/null @@ -1,45 +0,0 @@ -[Unit] -Description=Fingerprint Authentication Daemon -Documentation=man:fprintd(1) - -[Service] -Type=dbus -BusName=net.reactivated.Fprint -ExecStart=@libexecdir@/fprintd - -# Filesystem lockdown -ProtectSystem=strict -ProtectKernelTunables=true -ProtectKernelLogs=true -ProtectControlGroups=true -# This always corresponds to /var/lib/fprint -StateDirectory=fprint -StateDirectoryMode=0700 -ProtectHome=true -PrivateTmp=true - -SystemCallFilter=@system-service - -# Network -RestrictAddressFamilies=AF_UNIX AF_LOCAL AF_NETLINK - -# Execute Mappings -MemoryDenyWriteExecute=true - -# Modules -ProtectKernelModules=true - -# Real-time -RestrictRealtime=true - -# Privilege escalation -NoNewPrivileges=true - -# Protect clock, allow USB and SPI device access -ProtectClock=yes -DeviceAllow=char-usb_device rw -DeviceAllow=char-spi rw -DeviceAllow=char-hidraw rw - -# Allow tuning USB parameters (wakeup and persist) -ReadWritePaths=/sys/devices diff --git a/data/meson.build b/data/meson.build deleted file mode 100644 index ece5fc6..0000000 --- a/data/meson.build +++ /dev/null @@ -1,72 +0,0 @@ -install_data('net.reactivated.Fprint.conf', - install_dir: dbus_conf_dir) - -configure_file( - configuration: configuration_data({ - 'LIBEXECDIR': fprintd_installdir, - }), - input: 'net.reactivated.Fprint.service.in', - output: 'net.reactivated.Fprint.service', - install: true, - install_dir: dbus_service_dir, -) - -if get_option('systemd') - configure_file( - configuration: configuration_data({ - 'libexecdir': fprintd_installdir, - }), - input: 'fprintd.service.in', - output: 'fprintd.service', - install: true, - install_dir: systemd_unit_dir, - ) -endif - -polkit_policy = 'net.reactivated.fprint.device.policy' -polkit_policy_target = i18n.merge_file( - input: '@0@.in'.format(polkit_policy), - output: polkit_policy, - po_dir: meson.source_root() / 'po', - install: true, - install_dir: polkit_policy_directory, -) - -if xmllint.found() - test(polkit_policy, - xmllint, - depends: polkit_policy_target, - args: [ - '--noout', - polkit_policy_target.full_path(), - ]) -endif - -install_data('fprintd.conf', - install_dir: sysconfdir) - -if get_option('man') - manfiles = { - 'fprintd': 1, - 'pam_fprintd': 8, - } - - foreach man_name, man_section: manfiles - custom_target('man_' + man_name + '.' + man_section.to_string(), - input: man_name + '.pod', - output: man_name + '.' + man_section.to_string(), - command: [ - pod2man, - '-c', '', - '-s', man_section.to_string(), - '-q', 'none', - '-n', man_name, - '-r', 'freedesktop', - '@INPUT@', - '@OUTPUT@', - ], - install: true, - install_dir: datadir / 'man' / 'man' + man_section.to_string(), - ) - endforeach -endif diff --git a/data/net.reactivated.Fprint.conf b/data/net.reactivated.Fprint.conf deleted file mode 100644 index 633867c..0000000 --- a/data/net.reactivated.Fprint.conf +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/net.reactivated.Fprint.service.in b/data/net.reactivated.Fprint.service.in deleted file mode 100644 index 8402e2e..0000000 --- a/data/net.reactivated.Fprint.service.in +++ /dev/null @@ -1,5 +0,0 @@ -[D-BUS Service] -Name=net.reactivated.Fprint -Exec=@LIBEXECDIR@/fprintd -User=root -SystemdService=fprintd.service diff --git a/data/net.reactivated.fprint.device.policy.in b/data/net.reactivated.fprint.device.policy.in deleted file mode 100644 index 609a289..0000000 --- a/data/net.reactivated.fprint.device.policy.in +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - The FPrint Project - https://fprint.freedesktop.org/ - fprint - - - Verify a fingerprint - Privileges are required to verify fingerprints. - - no - no - yes - - - - - Enroll or Delete fingerprints - Privileges are required to enroll or delete fingerprints. - - no - no - auth_self_keep - - - - - Select a user to manage fingerprints for - Privileges are required to manage fingerprints for other users. - - no - no - auth_admin_keep - - - - diff --git a/data/pam_fprintd.8 b/data/pam_fprintd.8 deleted file mode 100644 index 9b000d4..0000000 --- a/data/pam_fprintd.8 +++ /dev/null @@ -1,184 +0,0 @@ -.\" Automatically generated by Pod::Man 4.12 (Pod::Simple 3.39) -.\" -.\" Standard preamble: -.\" ======================================================================== -.de Sp \" Vertical space (when we can't use .PP) -.if t .sp .5v -.if n .sp -.. -.de Vb \" Begin verbatim text -.ft CW -.nf -.ne \\$1 -.. -.de Ve \" End verbatim text -.ft R -.fi -.. -.\" Set up some character translations and predefined strings. \*(-- will -.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left -.\" double quote, and \*(R" will give a right double quote. \*(C+ will -.\" give a nicer C++. Capital omega is used to do unbreakable dashes and -.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, -.\" nothing in troff, for use with C<>. -.tr \(*W- -.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' -.ie n \{\ -. ds -- \(*W- -. ds PI pi -. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch -. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch -. ds L" "" -. ds R" "" -. ds C` -. ds C' -'br\} -.el\{\ -. ds -- \|\(em\| -. ds PI \(*p -. ds L" `` -. ds R" '' -. ds C` -. ds C' -'br\} -.\" -.\" Escape single quotes in literal strings from groff's Unicode transform. -.ie \n(.g .ds Aq \(aq -.el .ds Aq ' -.\" -.\" If the F register is >0, we'll generate index entries on stderr for -.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index -.\" entries marked with X<> in POD. Of course, you'll have to process the -.\" output yourself in some meaningful fashion. -.\" -.\" Avoid warning from groff about undefined register 'F'. -.de IX -.. -.nr rF 0 -.if \n(.g .if rF .nr rF 1 -.if (\n(rF:(\n(.g==0)) \{\ -. if \nF \{\ -. de IX -. tm Index:\\$1\t\\n%\t"\\$2" -.. -. if !\nF==2 \{\ -. nr % 0 -. nr F 2 -. \} -. \} -.\} -.rr rF -.\" -.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). -.\" Fear. Run. Save yourself. No user-serviceable parts. -. \" fudge factors for nroff and troff -.if n \{\ -. ds #H 0 -. ds #V .8m -. ds #F .3m -. ds #[ \f1 -. ds #] \fP -.\} -.if t \{\ -. ds #H ((1u-(\\\\n(.fu%2u))*.13m) -. ds #V .6m -. ds #F 0 -. ds #[ \& -. ds #] \& -.\} -. \" simple accents for nroff and troff -.if n \{\ -. ds ' \& -. ds ` \& -. ds ^ \& -. ds , \& -. ds ~ ~ -. ds / -.\} -.if t \{\ -. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" -. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' -. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' -. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' -. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' -. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' -.\} -. \" troff and (daisy-wheel) nroff accents -.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' -.ds 8 \h'\*(#H'\(*b\h'-\*(#H' -.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] -.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' -.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' -.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] -.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] -.ds ae a\h'-(\w'a'u*4/10)'e -.ds Ae A\h'-(\w'A'u*4/10)'E -. \" corrections for vroff -.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' -.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' -. \" for low resolution devices (crt and lpr) -.if \n(.H>23 .if \n(.V>19 \ -\{\ -. ds : e -. ds 8 ss -. ds o a -. ds d- d\h'-1'\(ga -. ds D- D\h'-1'\(hy -. ds th \o'bp' -. ds Th \o'LP' -. ds ae ae -. ds Ae AE -.\} -.rm #[ #] #H #V #F C -.\" ======================================================================== -.\" -.IX Title "pam_fprintd 8" -.TH pam_fprintd 8 "2020-01-24" "freedesktop" "" -.\" For nroff, turn off justification. Always turn off hyphenation; it makes -.\" way too many mistakes in technical documents. -.if n .ad l -.nh -.SH "NAME" -pam_fprintd \- PAM module to authenticate against fprintd, the fingerprint daemon -.SH "SYNOPSIS" -.IX Header "SYNOPSIS" -\&\fBpam_fprintd.so\fR [debug|debug=[\fIon\fR|\fIoff\fR|\fItrue\fR|\fIfalse\fR|\fI1\fR|\fI0\fR]] [max\-tries=\fI\s-1MAX_TRIES\s0\fR] [timeout=\fI\s-1TIMEOUT\s0\fR] -.SH "DESCRIPTION" -.IX Header "DESCRIPTION" -The pam_fprintd module is used to verify a user's fingerprints against fingerprints -enrolled using fprintd, the fingerprint management daemon. -.SH "OPTIONS" -.IX Header "OPTIONS" -.IP "\fBdebug\fR" 8 -.IX Item "debug" -.PD 0 -.IP "\fBdebug=[\f(BIon\fB|\f(BIoff\fB|\f(BItrue\fB|\f(BIfalse\fB|\f(BI1\fB|\f(BI0\fB]\fR" 8 -.IX Item "debug=[on|off|true|false|1|0]" -.PD -Whether debug should be turned on or off. Debug messages will be generated using -pam_syslog which means that they will be saved in the systemd journal by default. -.IP "\fBmax\-tries=\f(BI\s-1MAX_TRIES\s0\fB\fR" 8 -.IX Item "max-tries=MAX_TRIES" -The number of attempts at fingerprint authentication to try before returning an -authentication failure. The minimum, and default, number of tries is 3. -.IP "\fBtimeout=\f(BI\s-1TIMEOUT\s0\fB\fR" 8 -.IX Item "timeout=TIMEOUT" -The amount of time before returning an authentication failure. The default timeout -is 30 seconds, with 10 seconds being the minimum. -.SH "LIMITATIONS" -.IX Header "LIMITATIONS" -The \s-1PAM\s0 stack is by design a serialised authentication, so it is not -possible for pam_fprintd to allow authentication through passwords and -fingerprints at the same time. -.PP -It is up to the application using the \s-1PAM\s0 services to implement separate -\&\s-1PAM\s0 processes and run separate authentication stacks separately. This -is the way multiple authentication methods are made available to users -of gdm for example. -.SH "AUTHOR" -.IX Header "AUTHOR" -\&\fBfprintd\fR was written by Bastien Nocera. -.SH "SEE ALSO" -.IX Header "SEE ALSO" -.IP "\fBfprintd\fR, \fB\s-1PAM\s0\fR" 8 -.IX Item "fprintd, PAM" diff --git a/data/pam_fprintd.pod b/data/pam_fprintd.pod deleted file mode 100644 index 69b1a73..0000000 --- a/data/pam_fprintd.pod +++ /dev/null @@ -1,63 +0,0 @@ -=head1 NAME - -pam_fprintd - PAM module to authenticate against fprintd, the fingerprint daemon - -=head1 SYNOPSIS - -B [debug|debug=[I|I|I|I|I<1>|I<0>]] [max-tries=I] [timeout=I] - -=head1 DESCRIPTION - -The pam_fprintd module is used to verify a user's fingerprints against fingerprints -enrolled using fprintd, the fingerprint management daemon. - -=head1 OPTIONS - -=over 8 - -=item B - -=item B|I|I|I|I<1>|I<0>]> - -Whether debug should be turned on or off. Debug messages will be generated using -pam_syslog which means that they will be saved in the systemd journal by default. - -=item B> - -The number of attempts at fingerprint authentication to try before returning an -authentication failure. The minimum number of tries is 1 while the default is 3. - -=item B> - -The amount of time before returning an authentication failure. The default timeout -is 30 seconds, with 10 seconds being the minimum. - -=back - -=head1 LIMITATIONS - -=over 8 - -=back - -The PAM stack is by design a serialised authentication, so it is not -possible for pam_fprintd to allow authentication through passwords and -fingerprints at the same time. - -It is up to the application using the PAM services to implement separate -PAM processes and run separate authentication stacks separately. This -is the way multiple authentication methods are made available to users -of gdm for example. - -=head1 AUTHOR - -B was written by Bastien Nocera. - -=head1 SEE ALSO - -=over 8 - -=item B, B - -=back - diff --git a/doc/dbus/dbus-introspect-docs.dtd b/doc/dbus/dbus-introspect-docs.dtd deleted file mode 100644 index ca918fb..0000000 --- a/doc/dbus/dbus-introspect-docs.dtd +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/dbus/meson.build b/doc/dbus/meson.build deleted file mode 100644 index 802938e..0000000 --- a/doc/dbus/meson.build +++ /dev/null @@ -1,29 +0,0 @@ -docbook_xml_header = custom_target('docbook_xml_header', - output: 'docbook-xml-header.xml', - command: [ - 'echo', '-n', - '\n', - '\n', - ], - capture: true, -) - -dbus_interfaces_refs = [] -foreach interface_file: dbus_interfaces_files - basename = run_command('basename', interface_file.full_path(), '.xml').stdout().strip() - dbus_interfaces_refs += custom_target(basename + '_ref', - input: docbook_xml_header, - output: basename + '.ref.xml', - build_by_default: true, - depends: interface_file, - capture: true, - command: [ - bash, '-c', - 'cat @INPUT@;' + - xsltproc.path() + ' @0@/@1@ '.format( - meson.source_root(), - files('spec-to-docbook.xsl')[0]) + - interface_file.full_path() + '| tail -n +2;', - ], - ) -endforeach diff --git a/doc/dbus/spec-to-docbook.xsl b/doc/dbus/spec-to-docbook.xsl deleted file mode 100644 index 7a978de..0000000 --- a/doc/dbus/spec-to-docbook.xsl +++ /dev/null @@ -1,550 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - interface - - - - Methods - - - - - - - - - - - Signals - - - - - - - - - - - Implemented Interfaces - - Objects implementing also implements - org.freedesktop.DBus.Introspectable, - org.freedesktop.DBus.Properties - - - - - - - Properties - - - - - - - - - - - Description - - - - - - - Details - - - - - - - - - Signal Details - - - - - - - - - - - Property Details - - - - - - - - - - - - - - - - -: - - - - - - - - - - - - - - - - - - - - - - <anchor role="function"><xsl:attribute name="id"><xsl:value-of select="$basename"/>:<xsl:value-of select="@name"/></xsl:attribute></anchor>The "<xsl:value-of select="@name"/>" property - -'' - - - - - - - - - - - - - -: - - - - - - - - - - - - - - - - - - - - - <anchor role="function"><xsl:attribute name="id"><xsl:value-of select="$basename"/>::<xsl:value-of select="@name"/></xsl:attribute></anchor>The <xsl:value-of select="@name"/> signal - - () - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - : - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <anchor role="description"><xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute></anchor><xsl:value-of select="."/> - - - - - - - -Since - - - - - - - - /> - - - - - - - is deprecated since version and should not be used in newly-written code. Use - - - - - : - - - :: - - - . - - - - - - - - - - - - - - - -instead. - - - - - - - - - - - - - - - - - -See also: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -: - - - - - - - - - - - - Errors - - - - : - - - - - - - - - - - - Permissions - - - - - - - - - - - - - - - - - - <anchor role="function"><xsl:attribute name="id"><xsl:value-of select="$basename"/>.<xsl:value-of select="@name"/></xsl:attribute></anchor><xsl:value-of select="@name"/> () - - () - - - - - - - - - - - - - - - - -:'' - - - - - - - - - - - - -::() - - - - - - - - - - - - -.() - - - - - -'' -, - - - - - -'' -, - - - - - - -'' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/fprintd-docs.xml b/doc/fprintd-docs.xml deleted file mode 100644 index 6b4cad9..0000000 --- a/doc/fprintd-docs.xml +++ /dev/null @@ -1,82 +0,0 @@ - - -]> - - - fprintd Reference Manual - Version &version; - - - Bastien - Nocera - -
- hadess@hadess.net -
-
-
-
- - - 2008 - The fprintd Authors - - - - - Permission is granted to copy, distribute and/or modify this - document under the terms of the GNU Free - Documentation License, Version 1.1 or any later - version published by the Free Software Foundation with no - Invariant Sections, no Front-Cover Texts, and no Back-Cover - Texts. You may obtain a copy of the GNU Free - Documentation License from the Free Software - Foundation by visiting their Web site or by writing - to: - -
- The Free Software Foundation, Inc., - 59 Temple Place - Suite 330, - Boston, MA 02111-1307, - USA -
-
- - - Many of the names used by companies to distinguish their - products and services are claimed as trademarks. Where those - names appear in any GNOME documentation, and those trademarks - are made aware to the members of the GNOME Documentation - Project, the names have been printed in caps or initial caps. - -
-
- - - D-Bus API Reference - - - This part documents the D-Bus interface used to access the - fprintd daemon. - - - - - - - - Index - - - - - - License - -FIXME: MISSING XINCLUDE CONTENT - - -
diff --git a/doc/meson.build b/doc/meson.build deleted file mode 100644 index 27432b5..0000000 --- a/doc/meson.build +++ /dev/null @@ -1,27 +0,0 @@ -subdir('dbus') - -version_file = configure_file( - input: 'version.xml.in', - output: 'version.xml', - configuration: configuration_data({ - 'VERSION': meson.project_version(), - }), -) - -gnome.gtkdoc(meson.project_name(), - main_xml: 'fprintd-docs.xml', - src_dir: meson.source_root() / 'src', - dependencies: [ - declare_dependency( - sources: dbus_interfaces_refs, - link_with: libfprintd_private, - ), - ], - content_files: [ - version_file, - dbus_interfaces_refs, - ], - ignore_headers: [ - 'config.h', - ], - install: true) diff --git a/doc/version.xml.in b/doc/version.xml.in deleted file mode 100644 index d78bda9..0000000 --- a/doc/version.xml.in +++ /dev/null @@ -1 +0,0 @@ -@VERSION@ diff --git a/scripts/uncrustify.cfg b/scripts/uncrustify.cfg deleted file mode 100644 index 57d1532..0000000 --- a/scripts/uncrustify.cfg +++ /dev/null @@ -1,137 +0,0 @@ -newlines lf - -input_tab_size 8 -output_tab_size 8 - -string_escape_char 92 -string_escape_char2 0 - -# indenting -indent_columns 2 -indent_with_tabs 0 -indent_align_string True -indent_brace 2 -indent_braces false -indent_braces_no_func True -indent_func_call_param false -indent_func_def_param false -indent_func_proto_param false -indent_switch_case 0 -indent_case_brace 2 -indent_paren_close 1 - -# spacing -sp_arith Add -sp_assign Add -sp_enum_assign Add -sp_bool Add -sp_compare Add -sp_inside_paren Remove -sp_inside_fparens Remove -sp_func_def_paren Force -sp_func_proto_paren Force -sp_paren_paren Remove -sp_balance_nested_parens False -sp_paren_brace Remove -sp_before_square Remove -sp_before_squares Remove -sp_inside_square Remove -sp_before_ptr_star Add -sp_between_ptr_star Remove -sp_after_comma Add -sp_before_comma Remove -sp_after_cast Add -sp_sizeof_paren Add -sp_not Remove -sp_inv Remove -sp_addr Remove -sp_member Remove -sp_deref Remove -sp_sign Remove -sp_incdec Remove -sp_attribute_paren remove -sp_macro Force -sp_func_call_paren Force -sp_func_call_user_paren Remove -set func_call_user _ N_ C_ g_autoptr g_auto -sp_brace_typedef add -sp_cond_colon add -sp_cond_question add -sp_defined_paren remove - -# alignment -align_keep_tabs False -align_with_tabs False -align_on_tabstop False -align_number_right False -align_func_params True -align_var_def_span 0 -align_var_def_amp_style 1 -align_var_def_colon true -align_enum_equ_span 0 -align_var_struct_span 2 -align_var_def_star_style 2 -align_var_def_amp_style 2 -align_typedef_span 2 -align_typedef_func 0 -align_typedef_star_style 2 -align_typedef_amp_style 2 - -# newlines -nl_assign_leave_one_liners True -nl_enum_leave_one_liners False -nl_func_leave_one_liners False -nl_if_leave_one_liners False -nl_end_of_file Add -nl_assign_brace Remove -nl_func_var_def_blk 1 -nl_fcall_brace Add -nl_enum_brace Remove -nl_struct_brace Force -nl_union_brace Force -nl_if_brace Force -nl_brace_else Force -nl_elseif_brace Force -nl_else_brace Add -nl_for_brace Force -nl_while_brace Force -nl_do_brace Force -nl_brace_while Force -nl_switch_brace Force -nl_before_case True -nl_after_case False -nl_func_type_name Force -nl_func_proto_type_name Remove -nl_func_paren Remove -nl_func_decl_start Remove -nl_func_decl_args Force -nl_func_decl_end Remove -nl_fdef_brace Force -nl_after_return False -nl_define_macro False -nl_create_if_one_liner False -nl_create_for_one_liner False -nl_create_while_one_liner False -nl_after_semicolon True -nl_multi_line_cond true - -# mod -# I'd like these to be remove, but that removes brackets in if { if { foo } }, which i dislike -# Not clear what to do about that... -mod_full_brace_for Remove -mod_full_brace_if Remove -mod_full_brace_if_chain True -mod_full_brace_while Remove -mod_full_brace_do Remove -mod_full_brace_nl 3 -mod_paren_on_return Remove - -# line splitting -#code_width = 78 -ls_for_split_full True -ls_func_split_full True - -# positioning -pos_bool Trail -pos_conditional Trail - diff --git a/scripts/uncrustify.sh b/scripts/uncrustify.sh deleted file mode 100755 index a1cfc48..0000000 --- a/scripts/uncrustify.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -SRCROOT=`git rev-parse --show-toplevel` -CFG="$SRCROOT/scripts/uncrustify.cfg" -echo "srcroot: $SRCROOT" - -case "$1" in - -c|--check) - OPTS="--check" - ;; - *) - OPTS="--replace --no-backup" - ;; -esac - -pushd "$SRCROOT" -uncrustify -c "$CFG" $OPTS `git ls-tree --name-only -r HEAD | grep -E '.*\.[ch]$' | grep -v build/` -RES=$? -popd -exit $RES diff --git a/src/dbus-interactive-auth.patch b/src/dbus-interactive-auth.patch deleted file mode 100644 index 944089a..0000000 --- a/src/dbus-interactive-auth.patch +++ /dev/null @@ -1,110 +0,0 @@ ---- a/src/fprintd-dbus.c 2020-12-04 16:38:28.527712626 +0100 -+++ b/src/fprintd-dbus.c 2020-12-04 16:40:03.561692619 +0100 -@@ -1149,7 +1149,7 @@ - "ListEnrolledFingers", - g_variant_new ("(s)", - arg_username), -- G_DBUS_CALL_FLAGS_NONE, -+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, - -1, - cancellable, - callback, -@@ -1213,7 +1213,7 @@ - "ListEnrolledFingers", - g_variant_new ("(s)", - arg_username), -- G_DBUS_CALL_FLAGS_NONE, -+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, - -1, - cancellable, - error); -@@ -1253,7 +1253,7 @@ - "DeleteEnrolledFingers", - g_variant_new ("(s)", - arg_username), -- G_DBUS_CALL_FLAGS_NONE, -+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, - -1, - cancellable, - callback, -@@ -1312,7 +1312,7 @@ - "DeleteEnrolledFingers", - g_variant_new ("(s)", - arg_username), -- G_DBUS_CALL_FLAGS_NONE, -+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, - -1, - cancellable, - error); -@@ -1348,7 +1348,7 @@ - g_dbus_proxy_call (G_DBUS_PROXY (proxy), - "DeleteEnrolledFingers2", - g_variant_new ("()"), -- G_DBUS_CALL_FLAGS_NONE, -+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, - -1, - cancellable, - callback, -@@ -1404,7 +1404,7 @@ - _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), - "DeleteEnrolledFingers2", - g_variant_new ("()"), -- G_DBUS_CALL_FLAGS_NONE, -+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, - -1, - cancellable, - error); -@@ -1443,7 +1443,7 @@ - "Claim", - g_variant_new ("(s)", - arg_username), -- G_DBUS_CALL_FLAGS_NONE, -+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, - -1, - cancellable, - callback, -@@ -1502,7 +1502,7 @@ - "Claim", - g_variant_new ("(s)", - arg_username), -- G_DBUS_CALL_FLAGS_NONE, -+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, - -1, - cancellable, - error); -@@ -1633,7 +1633,7 @@ - "VerifyStart", - g_variant_new ("(s)", - arg_finger_name), -- G_DBUS_CALL_FLAGS_NONE, -+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, - -1, - cancellable, - callback, -@@ -1692,7 +1692,7 @@ - "VerifyStart", - g_variant_new ("(s)", - arg_finger_name), -- G_DBUS_CALL_FLAGS_NONE, -+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, - -1, - cancellable, - error); -@@ -1823,7 +1823,7 @@ - "EnrollStart", - g_variant_new ("(s)", - arg_finger_name), -- G_DBUS_CALL_FLAGS_NONE, -+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, - -1, - cancellable, - callback, -@@ -1882,7 +1882,7 @@ - "EnrollStart", - g_variant_new ("(s)", - arg_finger_name), -- G_DBUS_CALL_FLAGS_NONE, -+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, - -1, - cancellable, - error); diff --git a/src/device.c b/src/device.c deleted file mode 100644 index df1e782..0000000 --- a/src/device.c +++ /dev/null @@ -1,2763 +0,0 @@ -/* - * /net/reactivated/Fprint/Device/foo object implementation - * Copyright (C) 2008 Daniel Drake - * Copyright (C) 2020 Marco Trevisan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include "fprintd-dbus.h" -#include "fprintd.h" -#include "storage.h" - -#define VERIFY_STOP_DEVICE_WAIT 1 /* Seconds to wait for the device to complete */ - -static const char *FINGERS_NAMES[] = { - [FP_FINGER_UNKNOWN] = "unknown", - [FP_FINGER_LEFT_THUMB] = "left-thumb", - [FP_FINGER_LEFT_INDEX] = "left-index-finger", - [FP_FINGER_LEFT_MIDDLE] = "left-middle-finger", - [FP_FINGER_LEFT_RING] = "left-ring-finger", - [FP_FINGER_LEFT_LITTLE] = "left-little-finger", - [FP_FINGER_RIGHT_THUMB] = "right-thumb", - [FP_FINGER_RIGHT_INDEX] = "right-index-finger", - [FP_FINGER_RIGHT_MIDDLE] = "right-middle-finger", - [FP_FINGER_RIGHT_RING] = "right-ring-finger", - [FP_FINGER_RIGHT_LITTLE] = "right-little-finger" -}; - -static void fprint_device_dbus_skeleton_iface_init (FprintDBusDeviceIface *); -static gboolean action_authorization_handler (GDBusInterfaceSkeleton *, - GDBusMethodInvocation *, - gpointer user_data); - -static gboolean delete_enrolled_fingers (FprintDevice *rdev, - const char *user, - FpFinger finger, - GError **error); - -static GQuark quark_auth_user = 0; - -typedef enum { - ACTION_NONE = 0, - ACTION_IDENTIFY, - ACTION_VERIFY, - ACTION_ENROLL, - ACTION_OPEN, - ACTION_CLOSE, - ACTION_DELETE, -} FprintDeviceAction; - -typedef enum { - STATE_CLAIMED, - STATE_UNCLAIMED, - STATE_AUTO_CLAIM, - STATE_ANYTIME, -} FprintDeviceClaimState; - -typedef struct -{ - volatile gint _refcount; - - /* current method invocation */ - GDBusMethodInvocation *invocation; - - /* The current user of the device, if claimed */ - const char * const sender; - - /* The current user of the device, or if allowed, - * what was passed as a username argument */ - const char * const username; - - gboolean verify_status_reported; -} SessionData; - -typedef struct -{ - guint32 id; - FpDevice *dev; - SessionData *_session; - - gboolean local_storage_checked; - - guint verify_stop_wait_timeout_id; - - PolkitAuthority *auth; - - /* Hashtable of connected clients */ - GHashTable *clients; - - /* Required to restart the operation on a retry failure. */ - FpPrint *verify_data; - GPtrArray *identify_data; - int enroll_data; - - /* whether we're running an identify, or a verify */ - FprintDeviceAction current_action; - GCancellable *current_cancellable; - GDBusMethodInvocation *current_cancel_invocation; -} FprintDevicePrivate; - -G_DEFINE_TYPE_WITH_CODE (FprintDevice, fprint_device, - FPRINT_DBUS_TYPE_DEVICE_SKELETON, - G_ADD_PRIVATE (FprintDevice) - G_IMPLEMENT_INTERFACE (FPRINT_DBUS_TYPE_DEVICE, - fprint_device_dbus_skeleton_iface_init)); - -enum fprint_device_properties { - FPRINT_DEVICE_CONSTRUCT_DEV = 1, - FPRINT_DEVICE_BUSY, -}; - -enum fprint_device_signals { - SIGNAL_VERIFY_STATUS, - SIGNAL_VERIFY_FINGER_SELECTED, - SIGNAL_ENROLL_STATUS, - NUM_SIGNALS, -}; - -static guint32 last_id = ~0; -static guint signals[NUM_SIGNALS] = { 0, }; - -#ifndef POLKIT_HAS_AUTOPOINTERS -/* FIXME: Remove this once we're fine to depend on polkit 0.114 */ -G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAuthorizationResult, g_object_unref) -G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitSubject, g_object_unref) -#endif - -static void -session_data_unref (SessionData *session) -{ - if (g_atomic_int_dec_and_test (&session->_refcount)) - { - g_clear_pointer ((char **) &session->sender, g_free); - g_clear_pointer ((char **) &session->username, g_free); - g_clear_object (&session->invocation); - g_free (session); - } -} -G_DEFINE_AUTOPTR_CLEANUP_FUNC (SessionData, session_data_unref); - -static SessionData * -session_data_get (FprintDevicePrivate *priv) -{ - SessionData *invalid = (SessionData *) &priv->_session; - SessionData *cur; - - /* Get the current pointer and mark the pointer as "busy". */ - do - { - cur = g_atomic_pointer_get (&priv->_session); - /* Swap if cur is valid, otherwise busy loop. */ - } - while (cur == invalid || !g_atomic_pointer_compare_and_exchange (&priv->_session, cur, invalid)); - - /* We can safely increase the reference count now. */ - if (cur) - g_atomic_int_inc (&cur->_refcount); - - /* Swap back, this must succeed. */ - if (!g_atomic_pointer_compare_and_exchange (&priv->_session, invalid, cur)) - g_assert_not_reached (); - - return cur; -} - -/* Pass NULL sender and username to unset session data. */ -static SessionData * -session_data_set_new (FprintDevicePrivate *priv, gchar *sender, gchar *username) -{ - SessionData *invalid = (SessionData *) &priv->_session; - SessionData *new = NULL; - SessionData *old; - - g_assert ((!sender && !username) || (sender && username)); - if (sender) - { - new = g_new0 (SessionData, 1); - /* Internal reference of the pointer and returned reference. */ - new->_refcount = 2; - *(char **) &new->sender = sender; - *(char **) &new->username = username; - } - - /* Get the current (but not if it is busy) and put the new one in place. */ - do - { - old = g_atomic_pointer_get (&priv->_session); - /* Swap if old is valid, otherwise busy loop as someone is ref'ing it currently. */ - } - while (old == invalid || !g_atomic_pointer_compare_and_exchange (&priv->_session, old, new)); - - /* We can safely drop the our internal reference now. */ - if (old) - session_data_unref (old); - - return new; -} - -typedef FprintDevice FprintDeviceActionUnset; -static void -auto_device_action_unset (FprintDeviceActionUnset *self) -{ - FprintDevicePrivate *priv = fprint_device_get_instance_private (self); - - priv->current_action = ACTION_NONE; -} -G_DEFINE_AUTOPTR_CLEANUP_FUNC (FprintDeviceActionUnset, auto_device_action_unset); - -static void -fprint_device_dispose (GObject *object) -{ - FprintDevice *self = (FprintDevice *) object; - FprintDevicePrivate *priv = fprint_device_get_instance_private (self); - - g_hash_table_remove_all (priv->clients); - - G_OBJECT_CLASS (fprint_device_parent_class)->dispose (object); -} - -static void -fprint_device_finalize (GObject *object) -{ - FprintDevice *self = (FprintDevice *) object; - FprintDevicePrivate *priv = fprint_device_get_instance_private (self); - - g_clear_handle_id (&priv->verify_stop_wait_timeout_id, g_source_remove); - g_hash_table_destroy (priv->clients); - session_data_set_new (priv, NULL, NULL); - g_clear_object (&priv->auth); - g_clear_object (&priv->dev); - - if (priv->current_action != ACTION_NONE || - priv->_session || - priv->verify_data || - priv->identify_data || - priv->current_cancellable || - priv->current_cancel_invocation) - g_critical ("Device was not cleaned up properly before being finalized."); - - G_OBJECT_CLASS (fprint_device_parent_class)->finalize (object); -} - -static void -fprint_device_set_property (GObject *object, guint property_id, - const GValue *value, GParamSpec *pspec) -{ - FprintDevice *self = (FprintDevice *) object; - FprintDevicePrivate *priv = fprint_device_get_instance_private (self); - - switch (property_id) - { - case FPRINT_DEVICE_CONSTRUCT_DEV: - priv->dev = g_value_dup_object (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -fprint_device_get_property (GObject *object, guint property_id, - GValue *value, GParamSpec *pspec) -{ - FprintDevice *self = (FprintDevice *) object; - FprintDevicePrivate *priv = fprint_device_get_instance_private (self); - - switch (property_id) - { - case FPRINT_DEVICE_CONSTRUCT_DEV: - g_value_set_object (value, priv->dev); - break; - - case FPRINT_DEVICE_BUSY: - g_value_set_boolean (value, - g_hash_table_size (priv->clients) != 0 || - fp_device_get_temperature (priv->dev) > FP_TEMPERATURE_COLD); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -on_nr_enroll_stages_changed (FprintDevice *rdev, - GParamSpec *spec, - FpDevice *device) -{ - FprintDBusDevice *dbus_dev = FPRINT_DBUS_DEVICE (rdev); - gint nr_enroll_stages; - - nr_enroll_stages = fp_device_get_nr_enroll_stages (device); - - /* One extra step for our internal identification. */ - if (fp_device_has_feature (device, FP_DEVICE_FEATURE_IDENTIFY)) - nr_enroll_stages += 1; - - g_debug ("Device %s enroll stages changed to %d", - fp_device_get_name (device), - nr_enroll_stages); - - fprint_dbus_device_set_num_enroll_stages (dbus_dev, nr_enroll_stages); -} - -static void -on_scan_type_changed (FprintDevice *rdev, - GParamSpec *spec, - FpDevice *device) -{ - FprintDBusDevice *dbus_dev = FPRINT_DBUS_DEVICE (rdev); - - if (fp_device_get_scan_type (device) == FP_SCAN_TYPE_PRESS) - fprint_dbus_device_set_scan_type (dbus_dev, "press"); - else if (fp_device_get_scan_type (device) == FP_SCAN_TYPE_SWIPE) - fprint_dbus_device_set_scan_type (dbus_dev, "swipe"); - - g_debug ("Device %s scan type changed to '%s'", - fp_device_get_name (device), - fprint_dbus_device_get_scan_type (dbus_dev)); -} - -static void -on_finger_status_changed (FprintDevice *rdev, - GParamSpec *spec, - FpDevice *device) -{ - FprintDBusDevice *dbus_dev = FPRINT_DBUS_DEVICE (rdev); - FpFingerStatusFlags finger_status = fp_device_get_finger_status (device); - gboolean present, needed; - - present = !!(finger_status & FP_FINGER_STATUS_PRESENT); - fprint_dbus_device_set_finger_present (dbus_dev, present); - g_debug ("Finger present %d", present); - - needed = !!(finger_status & FP_FINGER_STATUS_NEEDED); - fprint_dbus_device_set_finger_needed (dbus_dev, needed); - g_debug ("Finger needed %d", needed); -} - -static void -on_temperature_changed (FprintDevice *rdev, - GParamSpec *spec, - FpDevice *device) -{ - g_object_notify (G_OBJECT (rdev), "busy"); -} - -static void -fprint_device_constructed (GObject *object) -{ - FprintDevice *rdev = FPRINT_DEVICE (object); - FprintDBusDevice *dbus_dev = FPRINT_DBUS_DEVICE (rdev); - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - fprint_dbus_device_set_name (dbus_dev, fp_device_get_name (priv->dev)); - - g_signal_connect_object (priv->dev, "notify::scan-type", - G_CALLBACK (on_scan_type_changed), - rdev, G_CONNECT_SWAPPED); - on_scan_type_changed (rdev, NULL, priv->dev); - - g_signal_connect_object (priv->dev, "notify::nr-enroll-stages", - G_CALLBACK (on_nr_enroll_stages_changed), - rdev, G_CONNECT_SWAPPED); - on_nr_enroll_stages_changed (rdev, NULL, priv->dev); - - g_signal_connect_object (priv->dev, "notify::finger-status", - G_CALLBACK (on_finger_status_changed), - rdev, G_CONNECT_SWAPPED); - on_finger_status_changed (rdev, NULL, priv->dev); - - g_signal_connect_object (priv->dev, "notify::temperature", - G_CALLBACK (on_temperature_changed), - rdev, G_CONNECT_SWAPPED); - on_temperature_changed (rdev, NULL, priv->dev); - - G_OBJECT_CLASS (fprint_device_parent_class)->constructed (object); -} - -static void -fprint_device_class_init (FprintDeviceClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GParamSpec *pspec; - - gobject_class->constructed = fprint_device_constructed; - gobject_class->dispose = fprint_device_dispose; - gobject_class->finalize = fprint_device_finalize; - gobject_class->set_property = fprint_device_set_property; - gobject_class->get_property = fprint_device_get_property; - - pspec = g_param_spec_object ("dev", "Device", - "Set device construction property", - FP_TYPE_DEVICE, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE); - g_object_class_install_property (gobject_class, - FPRINT_DEVICE_CONSTRUCT_DEV, pspec); - - pspec = g_param_spec_boolean ("busy", "Busy", - "Whether the device is in use or too warm", FALSE, - G_PARAM_READABLE); - g_object_class_install_property (gobject_class, - FPRINT_DEVICE_BUSY, pspec); - - signals[SIGNAL_VERIFY_STATUS] = - g_signal_lookup ("verify-status", FPRINT_TYPE_DEVICE); - signals[SIGNAL_ENROLL_STATUS] = - g_signal_lookup ("enroll-status", FPRINT_TYPE_DEVICE); - signals[SIGNAL_VERIFY_FINGER_SELECTED] = - g_signal_lookup ("verify-finger-selected", FPRINT_TYPE_DEVICE); - - quark_auth_user = g_quark_from_static_string ("authorized-user"); -} - -static void -_unwatch_name (gpointer id) -{ - g_bus_unwatch_name (GPOINTER_TO_INT (id)); -} - -static void -fprint_device_init (FprintDevice *device) -{ - FprintDevicePrivate *priv = fprint_device_get_instance_private (device); - - priv->id = ++last_id; - - /* Setup PolicyKit */ - priv->auth = polkit_authority_get_sync (NULL, NULL); - priv->clients = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - _unwatch_name); - - g_signal_connect (device, "g-authorize-method", - G_CALLBACK (action_authorization_handler), - NULL); -} - -FprintDevice * -fprint_device_new (FpDevice *dev) -{ - return g_object_new (FPRINT_TYPE_DEVICE, "dev", dev, NULL); -} - -guint32 -_fprint_device_get_id (FprintDevice *rdev) -{ - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - return priv->id; -} - -static void -suspend_cb (GObject *source_obj, - GAsyncResult *res, - gpointer user_data) -{ - g_autoptr(GTask) task = user_data; - GError *error = NULL; - - fp_device_suspend_finish (FP_DEVICE (source_obj), res, &error); - if (error) - g_task_return_error (task, error); - else - g_task_return_boolean (task, TRUE); -} - -static void -resume_cb (GObject *source_obj, - GAsyncResult *res, - gpointer user_data) -{ - g_autoptr(GTask) task = user_data; - GError *error = NULL; - - fp_device_resume_finish (FP_DEVICE (source_obj), res, &error); - if (error) - g_task_return_error (task, error); - else - g_task_return_boolean (task, TRUE); -} - -void -fprint_device_suspend (FprintDevice *rdev, - GAsyncReadyCallback callback, - void *user_data) -{ - GTask *task = NULL; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - /* Just forward to libfprint. */ - - task = g_task_new (rdev, NULL, callback, user_data); - fp_device_suspend (priv->dev, NULL, suspend_cb, task); -} - -void -fprint_device_resume (FprintDevice *rdev, - GAsyncReadyCallback callback, - void *user_data) -{ - GTask *task = NULL; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - /* Just forward to libfprint. */ - - task = g_task_new (rdev, NULL, callback, user_data); - fp_device_resume (priv->dev, NULL, resume_cb, task); -} - -void -fprint_device_suspend_finish (FprintDevice *rdev, - GAsyncResult *res, - GError **error) -{ - g_task_propagate_boolean (G_TASK (res), error); -} - -void -fprint_device_resume_finish (FprintDevice *rdev, - GAsyncResult *res, - GError **error) -{ - g_task_propagate_boolean (G_TASK (res), error); -} - - -static const char * -fp_finger_to_name (FpFinger finger) -{ - if (finger == FP_FINGER_UNKNOWN) - return "any"; - if (!FP_FINGER_IS_VALID (finger)) - return NULL; - return FINGERS_NAMES[finger]; -} - -static FpFinger -finger_name_to_fp_finger (const char *finger_name) -{ - FpFinger i; - - if (finger_name == NULL || *finger_name == '\0' || g_str_equal (finger_name, "any")) - return FP_FINGER_UNKNOWN; - - for (i = FP_FINGER_FIRST; i <= FP_FINGER_LAST; i++) - if (g_str_equal (finger_name, FINGERS_NAMES[i])) - return i; - - /* Invalid, let's try that */ - return FP_FINGER_UNKNOWN; -} - -static const char * -verify_result_to_name (gboolean match, GError *error) -{ - if (!error) - { - if (match) - return "verify-match"; - else - return "verify-no-match"; - } - - if (error->domain == FP_DEVICE_RETRY) - { - switch (error->code) - { - case FP_DEVICE_RETRY_TOO_SHORT: - return "verify-swipe-too-short"; - - case FP_DEVICE_RETRY_CENTER_FINGER: - return "verify-finger-not-centered"; - - case FP_DEVICE_RETRY_REMOVE_FINGER: - return "verify-remove-and-retry"; - - default: - return "verify-retry-scan"; - } - } - else - { - /* Which errors should be mapped to disconnection? - * Are drivers/libfprint/fprintd really in agreement here? - */ - if (g_error_matches (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_PROTO)) - return "verify-disconnected"; - else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) || - g_error_matches (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_NOT_FOUND)) - return "verify-no-match"; - - return "verify-unknown-error"; - } -} - -static const char * -enroll_result_to_name (gboolean completed, gboolean enrolled, GError *error) -{ - if (!error) - { - if (!completed) - return "enroll-stage-passed"; - else if (enrolled) - return "enroll-completed"; - else - return "enroll-failed"; - } - - if (error->domain == FP_DEVICE_RETRY) - { - switch (error->code) - { - case FP_DEVICE_RETRY_TOO_SHORT: - return "enroll-swipe-too-short"; - - case FP_DEVICE_RETRY_CENTER_FINGER: - return "enroll-finger-not-centered"; - - case FP_DEVICE_RETRY_REMOVE_FINGER: - return "enroll-remove-and-retry"; - - default: - return "enroll-retry-scan"; - } - } - else - { - /* Which errors should be mapped to disconnection? - * Are drivers/libfprint/fprintd really in agreement here? - */ - if (g_error_matches (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_PROTO)) - return "enroll-disconnected"; - else if (g_error_matches (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_FULL)) - return "enroll-data-full"; - else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return "enroll-failed"; - - return "enroll-unknown-error"; - } -} - -static FprintDevicePermission -get_permissions_for_invocation (GDBusMethodInvocation *invocation) -{ - FprintDevicePermission required_perms; - const char *method_name; - - required_perms = FPRINT_DEVICE_PERMISSION_NONE; - method_name = g_dbus_method_invocation_get_method_name (invocation); - - if (g_str_equal (method_name, "Claim")) - { - required_perms |= FPRINT_DEVICE_PERMISSION_VERIFY; - required_perms |= FPRINT_DEVICE_PERMISSION_ENROLL; - } - else if (g_str_equal (method_name, "DeleteEnrolledFinger")) - { - required_perms |= FPRINT_DEVICE_PERMISSION_ENROLL; - } - else if (g_str_equal (method_name, "DeleteEnrolledFingers")) - { - required_perms |= FPRINT_DEVICE_PERMISSION_ENROLL; - } - else if (g_str_equal (method_name, "DeleteEnrolledFingers2")) - { - required_perms |= FPRINT_DEVICE_PERMISSION_ENROLL; - } - else if (g_str_equal (method_name, "EnrollStart")) - { - required_perms |= FPRINT_DEVICE_PERMISSION_ENROLL; - } - else if (g_str_equal (method_name, "ListEnrolledFingers")) - { - required_perms |= FPRINT_DEVICE_PERMISSION_VERIFY; - } - else if (g_str_equal (method_name, "VerifyStart")) - { - required_perms |= FPRINT_DEVICE_PERMISSION_VERIFY; - } - else if (g_str_equal (method_name, "Release")) - { - } - else if (g_str_equal (method_name, "EnrollStop")) - { - } - else if (g_str_equal (method_name, "VerifyStop")) - { - /* Don't require permissiong for for release/stop operations. - * We are authenticated already if we could start, and we don't - * want to end up authorizing interactively again. - */ - } - else - { - g_assert_not_reached (); - } - - return required_perms; -} - -static FprintDeviceClaimState -get_claim_state_for_invocation (GDBusMethodInvocation *invocation) -{ - const char *method_name; - - method_name = g_dbus_method_invocation_get_method_name (invocation); - - if (g_str_equal (method_name, "Claim")) - return STATE_UNCLAIMED; - else if (g_str_equal (method_name, "DeleteEnrolledFingers")) - return STATE_AUTO_CLAIM; - else if (g_str_equal (method_name, "ListEnrolledFingers")) - return STATE_ANYTIME; - - return STATE_CLAIMED; -} - -static gboolean -_fprint_device_check_claimed (FprintDevice *rdev, - GDBusMethodInvocation *invocation, - GError **error) -{ - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - g_autoptr(SessionData) session = NULL; - FprintDeviceClaimState requested_state; - const char *sender; - - requested_state = get_claim_state_for_invocation (invocation); - - if (requested_state == STATE_ANYTIME) - return TRUE; - - session = session_data_get (priv); - if (requested_state == STATE_AUTO_CLAIM) - requested_state = session ? STATE_CLAIMED : STATE_UNCLAIMED; - - if (requested_state == STATE_UNCLAIMED) - { - /* Is it already claimed? */ - if (!session) - return TRUE; - - g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_ALREADY_IN_USE, - "Device was already claimed"); - return FALSE; - } - - g_assert (requested_state == STATE_CLAIMED); - - /* The device wasn't claimed, exit */ - if (session == NULL) - { - g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_CLAIM_DEVICE, - _("Device was not claimed before use")); - return FALSE; - } - - sender = g_dbus_method_invocation_get_sender (invocation); - - if (!g_str_equal (sender, session->sender) || session->invocation != NULL) - { - g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_ALREADY_IN_USE, - _("Device already in use by another user")); - return FALSE; - } - - return TRUE; -} - -static gboolean -_fprint_device_check_polkit_for_action (FprintDevice *rdev, - GDBusMethodInvocation *invocation, - const char *action, - GError **error) -{ - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - const char *sender; - - g_autoptr(GError) local_error = NULL; - g_autoptr(PolkitAuthorizationResult) result = NULL; - g_autoptr(PolkitSubject) subject = NULL; - - /* Check that caller is privileged */ - sender = g_dbus_method_invocation_get_sender (invocation); - subject = polkit_system_bus_name_new (sender); - - result = polkit_authority_check_authorization_sync (priv->auth, - subject, - action, - NULL, - POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, - NULL, &local_error); - if (result == NULL) - { - g_set_error (error, FPRINT_ERROR, - FPRINT_ERROR_PERMISSION_DENIED, - "Not Authorized: %s", local_error->message); - return FALSE; - } - - if (!polkit_authorization_result_get_is_authorized (result)) - { - g_set_error (error, FPRINT_ERROR, - FPRINT_ERROR_PERMISSION_DENIED, - "Not Authorized: %s", action); - return FALSE; - } - - return TRUE; -} - -static gboolean -fprint_device_check_polkit_for_permissions (FprintDevice *rdev, - GDBusMethodInvocation *invocation, - FprintDevicePermission permissions, - GError **error) -{ - g_autoptr(GFlagsClass) permission_flags = NULL; - unsigned i; - - if (permissions == FPRINT_DEVICE_PERMISSION_NONE) - return TRUE; - - permission_flags = g_type_class_ref (FPRINT_TYPE_DEVICE_PERMISSION); - - for (i = 0; i < permission_flags->n_values; ++i) - { - GFlagsValue *value = &permission_flags->values[i]; - const char *action; - - if (!(value->value & permissions)) - continue; - - action = value->value_nick; - g_debug ("Getting authorization to perform Polkit action %s", - action); - - g_clear_error (error); - if (_fprint_device_check_polkit_for_action (rdev, invocation, - action, error)) - return TRUE; - } - - g_assert (!error || *error); - return FALSE; -} - -static char * -_fprint_device_check_for_username (FprintDevice *rdev, - GDBusMethodInvocation *invocation, - const char *username, - GError **error) -{ - g_autoptr(GVariant) ret = NULL; - g_autoptr(GError) local_error = NULL; - GDBusConnection *connection; - const char *sender; - struct passwd *user; - guint32 uid; - - /* Get details about the current sender, and username/uid */ - connection = g_dbus_method_invocation_get_connection (invocation); - sender = g_dbus_method_invocation_get_sender (invocation); - - ret = g_dbus_connection_call_sync (connection, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetConnectionUnixUser", - g_variant_new ("(s)", sender), - NULL, G_DBUS_CALL_FLAGS_NONE, -1, - NULL, &local_error); - - if (!ret) - { - g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_INTERNAL, - "Could not get connection unix user ID: %s", - local_error->message); - return NULL; - } - - g_variant_get (ret, "(u)", &uid); - user = getpwuid (uid); - if (user == NULL) - { - g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_INTERNAL, - "Failed to get information about user UID %u", uid); - return NULL; - } - - /* The current user is usually allowed to access their - * own data, this should be followed by PolicyKit checks - * anyway */ - if (username == NULL || *username == '\0' || g_str_equal (username, user->pw_name)) - return g_strdup (user->pw_name); - - /* If we're not allowed to set a different username, - * then fail */ - if (!fprint_device_check_polkit_for_permissions (rdev, invocation, - FPRINT_DEVICE_PERMISSION_SETUSERNAME, - error)) - return NULL; - - return g_strdup (username); -} - -static void -_fprint_device_client_vanished (GDBusConnection *connection, - const char *name, - FprintDevice *rdev) -{ - g_autoptr(GError) error = NULL; - g_autoptr(SessionData) session = NULL; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - session = session_data_get (priv); - - /* Was that the client that claimed the device? */ - if (session != NULL && - g_strcmp0 (session->sender, name) == 0) - { - g_cancellable_cancel (priv->current_cancellable); - - if (!priv->current_cancellable) - { - /* This isn't optimal, but for verify/identify/enroll we expect the stop - * command. And we use current_cancellable as a flag to know that the - * underlying operation has finished already. - * If it has finished, unset the current_action. */ - switch (priv->current_action) - { - case ACTION_VERIFY: - case ACTION_IDENTIFY: - case ACTION_ENROLL: - priv->current_action = ACTION_NONE; - break; - - default: - break; - } - } - - while (priv->current_action != ACTION_NONE) - g_main_context_iteration (NULL, TRUE); - - /* The session may have disappeared at this point if the device - * was already closing. */ - g_clear_pointer (&session, session_data_unref); - session = session_data_get (priv); - if (session && !fp_device_close_sync (priv->dev, NULL, &error)) - g_critical ("Error closing device after disconnect: %s", error->message); - - session_data_set_new (priv, NULL, NULL); - } - g_hash_table_remove (priv->clients, name); - - if (g_hash_table_size (priv->clients) == 0) - g_object_notify (G_OBJECT (rdev), "busy"); -} - -static void -_fprint_device_add_client (FprintDevice *rdev, const char *sender) -{ - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - guint id; - - id = GPOINTER_TO_UINT (g_hash_table_lookup (priv->clients, sender)); - if (id == 0) - { - id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, - sender, - G_BUS_NAME_WATCHER_FLAGS_NONE, - NULL, - (GBusNameVanishedCallback) _fprint_device_client_vanished, - rdev, - NULL); - g_hash_table_insert (priv->clients, g_strdup (sender), GUINT_TO_POINTER (id)); - g_object_notify (G_OBJECT (rdev), "busy"); - } -} - -static void -dev_open_cb (FpDevice *dev, GAsyncResult *res, void *user_data) -{ - g_autoptr(GError) error = NULL; - FprintDevice *rdev = user_data; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - g_autoptr(SessionData) session = NULL; - g_autoptr(GDBusMethodInvocation) invocation = NULL; - g_autoptr(FprintDeviceActionUnset) action_unset = NULL; - - action_unset = rdev; - session = session_data_get (priv); - invocation = g_steal_pointer (&session->invocation); - - if (!fp_device_open_finish (dev, res, &error)) - { - g_autoptr(GError) dbus_error = NULL; - - dbus_error = g_error_new (FPRINT_ERROR, - FPRINT_ERROR_INTERNAL, - "Open failed with error: %s", error->message); - g_dbus_method_invocation_return_gerror (invocation, dbus_error); - session_data_set_new (priv, NULL, NULL); - return; - } - - g_debug ("claimed device %d", priv->id); - - fprint_dbus_device_complete_claim (FPRINT_DBUS_DEVICE (rdev), - invocation); -} - -static gboolean -fprintd_device_authorize_user (FprintDevice *rdev, - GDBusMethodInvocation *invocation, - GError **error) -{ - GVariant *params = NULL; - const char *username = NULL; - g_autofree char *user = NULL; - - params = g_dbus_method_invocation_get_parameters (invocation); - g_assert (g_variant_n_children (params) == 1); - g_variant_get (params, "(&s)", &username); - g_assert (username); - - user = _fprint_device_check_for_username (rdev, - invocation, - username, - error); - if (user == NULL) - return FALSE; - - /* We keep the user attached to the invocation as it may not be the same - * of the requested one, in case an empty one was passed. - * Given that now we may have multiple cuncurrent requests, it wouldn't - * be safe to add another member to the priv, as it would need even more - * multi-thread checks around, and over-complicate things. - */ - g_object_set_qdata_full (G_OBJECT (invocation), quark_auth_user, - g_steal_pointer (&user), g_free); - - return TRUE; -} - -static gboolean -fprint_device_claim (FprintDBusDevice *dbus_dev, - GDBusMethodInvocation *invocation, - const char *username) -{ - FprintDevice *rdev = FPRINT_DEVICE (dbus_dev); - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - g_autoptr(SessionData) session = NULL; - g_autoptr(GError) error = NULL; - char *sender, *user; - - if (!_fprint_device_check_claimed (rdev, invocation, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - user = g_object_steal_qdata (G_OBJECT (invocation), quark_auth_user); - g_assert (user); - g_assert (g_str_equal (username, "") || g_str_equal (user, username)); - - sender = g_strdup (g_dbus_method_invocation_get_sender (invocation)); - _fprint_device_add_client (rdev, sender); - - session = session_data_set_new (priv, g_steal_pointer (&sender), g_steal_pointer (&user)); - session->invocation = g_object_ref (invocation); - - g_debug ("user '%s' claiming the device: %d", session->username, priv->id); - - priv->current_action = ACTION_OPEN; - fp_device_open (priv->dev, NULL, (GAsyncReadyCallback) dev_open_cb, rdev); - - return TRUE; -} - -static void -dev_close_cb (FpDevice *dev, GAsyncResult *res, void *user_data) -{ - g_autoptr(GError) error = NULL; - FprintDevice *rdev = user_data; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - g_autoptr(SessionData) session = NULL; - g_autoptr(GDBusMethodInvocation) invocation = NULL; - g_autoptr(FprintDeviceActionUnset) action_unset = NULL; - - session = session_data_get (priv); - session_data_set_new (priv, NULL, NULL); - invocation = g_steal_pointer (&session->invocation); - action_unset = rdev; - - if (!fp_device_close_finish (dev, res, &error)) - { - g_autoptr(GError) dbus_error = NULL; - - dbus_error = g_error_new (FPRINT_ERROR, - FPRINT_ERROR_INTERNAL, - "Release failed with error: %s", error->message); - g_dbus_method_invocation_return_gerror (invocation, dbus_error); - return; - } - - g_debug ("released device %d", priv->id); - - fprint_dbus_device_complete_release (FPRINT_DBUS_DEVICE (rdev), - invocation); -} - -static gboolean -fprint_device_release (FprintDBusDevice *dbus_dev, - GDBusMethodInvocation *invocation) -{ - g_autoptr(GError) error = NULL; - g_autoptr(SessionData) session = NULL; - FprintDevice *rdev = FPRINT_DEVICE (dbus_dev); - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - if (!_fprint_device_check_claimed (rdev, invocation, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - if (priv->current_cancellable) - { - if (priv->current_action == ACTION_ENROLL) - g_warning ("Enrollment was in progress, stopping it"); - else if (priv->current_action == ACTION_IDENTIFY || - priv->current_action == ACTION_VERIFY) - g_warning ("Verification was in progress, stopping it"); - else if (priv->current_action == ACTION_DELETE) - g_warning ("Deletion was in progress, stopping it"); - - g_cancellable_cancel (priv->current_cancellable); - while (priv->current_action != ACTION_NONE) - g_main_context_iteration (NULL, TRUE); - } - - session = session_data_get (priv); - - /* We iterated the mainloop, the session may have disappeared already. */ - if (!session) - { - fprint_dbus_device_complete_release (FPRINT_DBUS_DEVICE (rdev), invocation); - return TRUE; - } - - session->invocation = g_object_ref (invocation); - - priv->current_action = ACTION_CLOSE; - fp_device_close (priv->dev, NULL, (GAsyncReadyCallback) dev_close_cb, rdev); - - return TRUE; -} - -/* NOTE: This should probably be moved to the storage layer. */ -static GPtrArray * -load_user_prints (FprintDevice *rdev, - const char *username) -{ - g_autoptr(GPtrArray) res = g_ptr_array_new_with_free_func (g_object_unref); - g_autoptr(GSList) fingers = NULL; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - GSList *l; - - fingers = store.discover_prints (priv->dev, username); - - for (l = fingers; l; l = l->next) - { - g_autoptr(FpPrint) print = NULL; - - store.print_data_load (priv->dev, - GPOINTER_TO_UINT (l->data), - username, - &print); - - if (!print) - continue; - - g_ptr_array_add (res, g_steal_pointer (&print)); - } - - return g_steal_pointer (&res); -} - -static GPtrArray * -load_all_prints (FprintDevice *rdev) -{ - g_autoptr(GPtrArray) res = g_ptr_array_new_with_free_func (g_object_unref); - GSList *user, *users = NULL; - guint i; - - users = store.discover_users (); - - for (user = users; user; user = user->next) - { - const char *username = user->data; - g_autoptr(GPtrArray) prints = load_user_prints (rdev, username); - - /* TODO: Use g_ptr_array_extend_and_steal with GLib >= 2.62 */ - for (i = 0; i < prints->len; i++) - g_ptr_array_add (res, g_object_ref (g_ptr_array_index (prints, i))); - } - - g_slist_free_full (users, g_free); - - return g_steal_pointer (&res); -} - -static void -report_verify_status (FprintDevice *rdev, - gboolean match, - GError *error) -{ - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - const char *result = verify_result_to_name (match, error); - - g_autoptr(SessionData) session = NULL; - gboolean done; - - done = (error == NULL || error->domain != FP_DEVICE_RETRY); - - session = session_data_get (priv); - - if (done && session->verify_status_reported) - { - /* It is completely fine for cancellation to occur after a - * result has been reported. */ - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("Verify status already reported. Ignoring %s", result); - return; - } - - g_debug ("report_verify_status: result %s", result); - g_signal_emit (rdev, signals[SIGNAL_VERIFY_STATUS], 0, result, done); - - if (done) - session->verify_status_reported = TRUE; -} - -static void -check_local_storage (FprintDevice *rdev, - gboolean found_match, - GError *error) -{ - g_autoptr(GError) err = NULL; - g_autoptr(GPtrArray) device_prints = NULL; - g_autoptr(GPtrArray) host_prints = NULL; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - unsigned i; - - g_return_if_fail (priv->current_action == ACTION_VERIFY || - priv->current_action == ACTION_IDENTIFY); - - /* This only ever sense if the device can list prints. */ - if (!fp_device_has_feature (priv->dev, FP_DEVICE_FEATURE_STORAGE_LIST)) - return; - - /* We do not have any proper driver that correctly reports DATA_NOT_FOUND - * errors. Only synaptics, but there the feature is being disabled on the - * firmware side. - * As such, just always run a test the first time we get a match failure. - */ - if (g_error_matches (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_NOT_FOUND)) - { - if (priv->local_storage_checked) - g_warning ("Device %s reported that a passed print did not exist during action %d, but we verified the local storage!", - fp_device_get_name (priv->dev), priv->current_action); - else - g_debug ("Device %s reported that a passed print did not exist during action %d", - fp_device_get_name (priv->dev), priv->current_action); - } - else if (error || priv->local_storage_checked) - { - return; - } - else if (!found_match) - { - g_debug ("Device %s failed to match during action %d, verifying local storage", - fp_device_get_name (priv->dev), priv->current_action); - } - else - { - return; - } - - priv->local_storage_checked = TRUE; - - device_prints = fp_device_list_prints_sync (priv->dev, NULL, &err); - if (!device_prints) - { - g_warning ("Failed to query prints: %s", err->message); - return; - } - - host_prints = load_all_prints (rdev); - - for (i = 0; i < host_prints->len; i++) - { - FpPrint *print = g_ptr_array_index (host_prints, i); - int r; - - if (g_ptr_array_find_with_equal_func (device_prints, - print, - (GEqualFunc) fp_print_equal, - NULL)) - continue; - - /* Print not known by device, remove locally */ - if ((r = store.print_data_delete (priv->dev, - fp_print_get_finger (print), - fp_print_get_username (print))) == 0) - { - g_message ("Deleted stored finger %d for user %s as it is unknown to device.", - fp_print_get_finger (print), - fp_print_get_username (print)); - } - else - { - g_warning ("Error deleting finger %d for user %s that is unknown to device: %d!", - fp_print_get_finger (print), - fp_print_get_username (print), - r); - } - } -} - -static gboolean -can_start_action (FprintDevice *rdev, GError **error) -{ - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - switch (priv->current_action) - { - case ACTION_NONE: - return TRUE; - - case ACTION_ENROLL: - g_set_error (error, - FPRINT_ERROR, FPRINT_ERROR_ALREADY_IN_USE, - "Enrollment already in progress"); - break; - - case ACTION_IDENTIFY: - case ACTION_VERIFY: - g_set_error (error, - FPRINT_ERROR, FPRINT_ERROR_ALREADY_IN_USE, - "Verification already in progress"); - break; - - case ACTION_OPEN: - g_set_error (error, - FPRINT_ERROR, FPRINT_ERROR_ALREADY_IN_USE, - "Claim already in progress"); - break; - - case ACTION_CLOSE: - g_set_error (error, - FPRINT_ERROR, FPRINT_ERROR_ALREADY_IN_USE, - "Release already in progress"); - break; - - case ACTION_DELETE: - g_set_error (error, - FPRINT_ERROR, FPRINT_ERROR_ALREADY_IN_USE, - "Delete already in progress"); - break; - - default: /* Fallback only. */ - g_assert_not_reached (); - g_set_error (error, - FPRINT_ERROR, FPRINT_ERROR_ALREADY_IN_USE, - "Another operation is already in progress"); - } - - return FALSE; -} - -static gboolean -can_stop_action (FprintDevice *rdev, - FprintDeviceAction action, - GError **error) -{ - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - gboolean action_matches; - - switch (priv->current_action) - { - case ACTION_IDENTIFY: - case ACTION_VERIFY: - action_matches = (action == ACTION_VERIFY || action == ACTION_IDENTIFY); - break; - - default: - action_matches = priv->current_action == action; - } - - if (action_matches && !priv->current_cancel_invocation) - return TRUE; - - if (priv->current_action != ACTION_NONE || action_matches) - { - g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_ALREADY_IN_USE, - "Another operation is already in progress"); - return FALSE; - } - - switch (action) - { - case ACTION_ENROLL: - g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_NO_ACTION_IN_PROGRESS, - "No enrollment in progress"); - return FALSE; - - case ACTION_VERIFY: - g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_NO_ACTION_IN_PROGRESS, - "No verification in progress"); - return FALSE; - - default: - g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_NO_ACTION_IN_PROGRESS, - "No action in progress"); - return FALSE; - } -} - -static void -stoppable_action_completed (FprintDevice *rdev) -{ - g_autoptr(SessionData) session = NULL; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - FprintDBusDevice *dbus_dev = FPRINT_DBUS_DEVICE (rdev); - - session = session_data_get (priv); - - /* Return the cancellation or reset action right away if vanished. */ - if (priv->current_cancel_invocation) - { - switch (priv->current_action) - { - case ACTION_VERIFY: - case ACTION_IDENTIFY: - fprint_dbus_device_complete_verify_stop (dbus_dev, - g_steal_pointer (&priv->current_cancel_invocation)); - break; - - case ACTION_ENROLL: - fprint_dbus_device_complete_enroll_stop (dbus_dev, - g_steal_pointer (&priv->current_cancel_invocation)); - break; - - default: - g_assert_not_reached (); - } - - priv->current_action = ACTION_NONE; - session->verify_status_reported = FALSE; - } - else if (g_cancellable_is_cancelled (priv->current_cancellable)) - { - priv->current_action = ACTION_NONE; - session->verify_status_reported = FALSE; - } - - g_clear_object (&priv->current_cancellable); -} - -static void -stoppable_action_stop (FprintDevice *rdev, - GDBusMethodInvocation *invocation) -{ - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - g_assert (priv->current_cancel_invocation == NULL); - - priv->current_cancel_invocation = invocation; - - /* We return only when the action was cancelled */ - if (priv->current_cancellable) - g_cancellable_cancel (priv->current_cancellable); - else - stoppable_action_completed (rdev); -} - -static void -match_cb (FpDevice *device, - FpPrint *match, - FpPrint *print, - gpointer user_data, - GError *error) -{ - FprintDevice *rdev = user_data; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - gboolean matched; - gboolean cancelled; - - g_assert_true (error == NULL || error->domain == FP_DEVICE_RETRY); - - cancelled = g_cancellable_is_cancelled (priv->current_cancellable); - matched = match != NULL && cancelled == FALSE; - - report_verify_status (rdev, matched, error); -} - -static void -verify_cb (FpDevice *dev, GAsyncResult *res, void *user_data) -{ - g_autoptr(GError) error = NULL; - FprintDevice *rdev = user_data; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - gboolean success; - const char *name; - gboolean match; - - success = fp_device_verify_finish (dev, res, &match, NULL, &error); - g_assert (!!success == !error); - name = verify_result_to_name (match, error); - - g_debug ("verify_cb: result %s", name); - - /* Automatically restart the operation for retry failures */ - if (error && error->domain == FP_DEVICE_RETRY) - { - fp_device_verify (priv->dev, - priv->verify_data, - priv->current_cancellable, - match_cb, rdev, NULL, - (GAsyncReadyCallback) verify_cb, - rdev); - } - else - { - g_clear_object (&priv->verify_data); - - if (error) - { - report_verify_status (rdev, FALSE, error); - - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("Device reported an error during verify: %s", - error->message); - } - - check_local_storage (rdev, match, error); - - stoppable_action_completed (rdev); - } -} - -static void -identify_cb (FpDevice *dev, GAsyncResult *res, void *user_data) -{ - g_autoptr(GError) error = NULL; - g_autoptr(FpPrint) match = NULL; - FprintDevice *rdev = user_data; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - const char *name; - gboolean success; - - success = fp_device_identify_finish (dev, res, &match, NULL, &error); - g_assert (!!success == !error); - name = verify_result_to_name (match != NULL, error); - - g_debug ("identify_cb: result %s", name); - - /* Automatically restart the operation for retry failures */ - if (error && error->domain == FP_DEVICE_RETRY) - { - fp_device_identify (priv->dev, - priv->identify_data, - priv->current_cancellable, - match_cb, rdev, NULL, - (GAsyncReadyCallback) identify_cb, - rdev); - } - else - { - g_clear_pointer (&priv->identify_data, g_ptr_array_unref); - - if (error) - { - report_verify_status (rdev, FALSE, error); - - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("Device reported an error during identify: %s", - error->message); - } - - check_local_storage (rdev, match != NULL, error); - - stoppable_action_completed (rdev); - } -} - -static gboolean -fprint_device_verify_start (FprintDBusDevice *dbus_dev, - GDBusMethodInvocation *invocation, - const char *finger_name) -{ - FprintDevice *rdev = FPRINT_DEVICE (dbus_dev); - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - g_autoptr(GPtrArray) gallery = NULL; - g_autoptr(SessionData) session = NULL; - g_autoptr(GError) error = NULL; - FpFinger finger = finger_name_to_fp_finger (finger_name); - - if (!_fprint_device_check_claimed (rdev, invocation, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - session = session_data_get (priv); - - if (!can_start_action (rdev, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - if (finger == FP_FINGER_UNKNOWN) - { - gallery = load_user_prints (rdev, session->username); - - if (!gallery->len) - { - g_set_error (&error, FPRINT_ERROR, FPRINT_ERROR_NO_ENROLLED_PRINTS, - "No fingerprints enrolled"); - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - else if (gallery->len == 1) - { - FpPrint *fprint = g_ptr_array_index (gallery, 0); - - /* If we only have a single finger, then do verify on that. - * This also means we report it back correctly to the user. */ - finger = fp_print_get_finger (fprint); - } - else if (fp_device_has_feature (priv->dev, FP_DEVICE_FEATURE_IDENTIFY)) - { - guint i; - - for (i = 0; i < gallery->len; i++) - { - FpPrint *fprint = g_ptr_array_index (gallery, i); - - g_debug ("adding finger %s to the gallery", - fp_finger_to_name (fp_print_get_finger (fprint))); - } - } - } - - if (fp_device_has_feature (priv->dev, FP_DEVICE_FEATURE_IDENTIFY) && - finger == FP_FINGER_UNKNOWN) - { - priv->current_action = ACTION_IDENTIFY; - - g_debug ("start identification device %d", priv->id); - priv->current_cancellable = g_cancellable_new (); - priv->identify_data = g_ptr_array_ref (gallery); - fp_device_identify (priv->dev, gallery, priv->current_cancellable, - match_cb, rdev, NULL, - (GAsyncReadyCallback) identify_cb, rdev); - } - else - { - g_autoptr(FpPrint) print = NULL; - - if (gallery) - { - /* TODO: Use g_ptr_array_remove_index_fast with GLib >= 2.58 */ - print = g_object_ref (g_ptr_array_index (gallery, 0)); - g_ptr_array_remove_index_fast (gallery, 0); - finger = fp_print_get_finger (print); - } - else - { - store.print_data_load (priv->dev, finger, session->username, &print); - } - - if (!print) - { - g_set_error (&error, FPRINT_ERROR, FPRINT_ERROR_NO_ENROLLED_PRINTS, - "No such print %d", finger); - g_dbus_method_invocation_return_gerror (invocation, - error); - return TRUE; - } - - g_debug ("start verification device %d finger %s", priv->id, - fp_finger_to_name (finger)); - - priv->current_action = ACTION_VERIFY; - priv->current_cancellable = g_cancellable_new (); - priv->verify_data = g_object_ref (print); - fp_device_verify (priv->dev, print, priv->current_cancellable, - match_cb, rdev, NULL, - (GAsyncReadyCallback) verify_cb, rdev); - } - - fprint_dbus_device_complete_verify_start (dbus_dev, invocation); - - /* Emit VerifyFingerSelected telling the front-end which finger - * we selected for auth */ - g_signal_emit (rdev, signals[SIGNAL_VERIFY_FINGER_SELECTED], - 0, fp_finger_to_name (finger)); - - return TRUE; -} - -static gboolean -verify_stop_wait_timeout (gpointer data) -{ - guint *timeout_id = data; - - *timeout_id = 0; - return FALSE; -} - -static gboolean -verify_has_completed (FprintDevice *rdev) -{ - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - if (!priv->current_cancellable || - g_cancellable_is_cancelled (priv->current_cancellable)) - return TRUE; - - switch (priv->current_action) - { - case ACTION_VERIFY: - return !priv->verify_data; - - case ACTION_IDENTIFY: - return !priv->identify_data; - - default: - g_assert_not_reached (); - } -} - -static gboolean -fprint_device_verify_stop (FprintDBusDevice *dbus_dev, - GDBusMethodInvocation *invocation) -{ - FprintDevice *rdev = FPRINT_DEVICE (dbus_dev); - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - g_autoptr(GError) error = NULL; - - if (!_fprint_device_check_claimed (rdev, invocation, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - if (!can_stop_action (rdev, ACTION_VERIFY, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - if (!verify_has_completed (rdev)) - { - g_autoptr(SessionData) session = session_data_get (priv); - - if (session->verify_status_reported) - { - /* If we got a status report we need to delay the cancellation - * of the action, leaving the device some more time to complete - * the operation (and in case return the real error) before proceed - * in cancelling it. - * In case Release or client vanished while waiting the invocation - * will be handled by stoppable_action_completed() during cancellation - */ - g_assert (priv->verify_stop_wait_timeout_id == 0); - - priv->verify_stop_wait_timeout_id = - g_timeout_add_seconds (VERIFY_STOP_DEVICE_WAIT, verify_stop_wait_timeout, - &priv->verify_stop_wait_timeout_id); - - g_assert (priv->current_cancel_invocation == NULL); - priv->current_cancel_invocation = invocation; - - while (priv->verify_stop_wait_timeout_id && !verify_has_completed (rdev)) - g_main_context_iteration (NULL, TRUE); - - g_clear_handle_id (&priv->verify_stop_wait_timeout_id, g_source_remove); - - if (!priv->current_cancel_invocation) - return TRUE; - - priv->current_cancel_invocation = NULL; - } - } - - stoppable_action_stop (rdev, invocation); - - return TRUE; -} - -static void -enroll_progress_cb (FpDevice *dev, - gint completed_stages, - FpPrint *print, - gpointer user_data, - GError *error) -{ - FprintDevice *rdev = user_data; - const char *name = enroll_result_to_name (FALSE, FALSE, error); - - g_debug ("enroll_stage_cb: result %s", name); - - /* NOTE: We add one more step internally, but we can ignore that here. */ - if (completed_stages < fp_device_get_nr_enroll_stages (dev)) - g_signal_emit (rdev, signals[SIGNAL_ENROLL_STATUS], 0, name, FALSE); -} - -static gint -garbage_collect_sort (gconstpointer a, gconstpointer b) -{ - /* Prefer */ - FpPrint *print_a = *((FpPrint **) a); - FpPrint *print_b = *((FpPrint **) b); - const GDate *date_a, *date_b; - guint32 julian_a = 0; - guint32 julian_b = 0; - gint32 rand_a, rand_b; - - date_a = fp_print_get_enroll_date (print_a); - date_b = fp_print_get_enroll_date (print_b); - - if (date_a && g_date_valid (date_a)) - julian_a = g_date_get_julian (date_a); - if (date_b && g_date_valid (date_b)) - julian_b = g_date_get_julian (date_b); - - /* Sort older prints first. */ - if (julian_a != julian_b) - return julian_b - julian_a; - - /* Randomize the order, - * do so by sorting on a random number we assign to each print. - * Not nice, but gets the job done. - */ - rand_a = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (print_a), "sort-rand")); - rand_b = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (print_b), "sort-rand")); - if (rand_a == 0) - { - rand_a = g_random_int_range (1, G_MAXINT32); - g_object_set_data (G_OBJECT (print_a), "sort-rand", GINT_TO_POINTER (rand_a)); - } - - if (rand_b == 0) - { - rand_b = g_random_int_range (1, G_MAXINT32); - g_object_set_data (G_OBJECT (print_b), "sort-rand", GINT_TO_POINTER (rand_b)); - } - - return rand_b - rand_a; -} - -static gboolean -try_delete_print (FprintDevice *rdev) -{ - g_autoptr(GError) error = NULL; - g_autoptr(GPtrArray) device_prints = NULL; - g_autoptr(GPtrArray) host_prints = NULL; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - guint i; - - device_prints = fp_device_list_prints_sync (priv->dev, NULL, &error); - if (!device_prints) - { - g_warning ("Failed to query prints: %s", error->message); - return FALSE; - } - - g_debug ("Device has %d prints stored", device_prints->len); - - /* Sort in order of preferred garbage collection. - * With randomization if we can't sort them. */ - g_ptr_array_sort (device_prints, garbage_collect_sort); - - host_prints = load_all_prints (rdev); - - for (i = 0; i < host_prints->len; i++) - { - guint index; - - if (!g_ptr_array_find_with_equal_func (device_prints, - g_ptr_array_index (host_prints, i), - (GEqualFunc) fp_print_equal, - &index)) - continue; - - /* Found an equal print, remove it */ - g_ptr_array_remove_index (device_prints, index); - } - - g_debug ("Device has %d prints stored that we do not need", device_prints->len); - if (device_prints->len == 0) - return FALSE; - - /* Just delete the first print in the list at this point. - * We could be smarter and fetch some more metadata. */ - fp_device_delete_print_sync (priv->dev, - g_ptr_array_index (device_prints, 0), - NULL, - &error); - - if (error) - { - g_warning ("Failed to garbage collect a print: %s", error->message); - return FALSE; - } - - return TRUE; -} - -#if !GLIB_CHECK_VERSION (2, 63, 3) -G_DEFINE_AUTOPTR_CLEANUP_FUNC (GDate, g_date_free); -#endif - -static FpPrint * -fprint_device_create_enroll_template (FprintDevice *rdev, FpFinger finger) -{ - g_autoptr(SessionData) session = NULL; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - g_autoptr(GDateTime) datetime = NULL; - g_autoptr(GDate) date = NULL; - FpPrint *template = NULL; - gint year, month, day; - - session = session_data_get (priv); - - template = fp_print_new (priv->dev); - fp_print_set_finger (template, finger); - fp_print_set_username (template, session->username); - datetime = g_date_time_new_now_local (); - g_date_time_get_ymd (datetime, &year, &month, &day); - date = g_date_new_dmy (day, month, year); - fp_print_set_enroll_date (template, date); - - return template; -} - -static void -enroll_cb (FpDevice *dev, GAsyncResult *res, void *user_data) -{ - g_autoptr(GError) error = NULL; - FprintDevice *rdev = user_data; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - g_autoptr(FpPrint) print = NULL; - const char *name; - - print = fp_device_enroll_finish (dev, res, &error); - - /* We need to special case the issue where the on device storage - * is completely full. In that case, we check whether we can delete - * a print that is not coming from us; assuming it is from an old - * installation. - * We do this synchronously, which is not great but should be good - * enough. */ - if (g_error_matches (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_FULL)) - { - g_debug ("Device storage is full"); - if (fp_device_has_feature (priv->dev, FP_DEVICE_FEATURE_STORAGE_LIST) && - try_delete_print (rdev)) - { - /* Success? Then restart the operation */ - fp_device_enroll (priv->dev, - fprint_device_create_enroll_template (rdev, priv->enroll_data), - priv->current_cancellable, - enroll_progress_cb, - rdev, - NULL, - (GAsyncReadyCallback) enroll_cb, - rdev); - return; - } - } - - name = enroll_result_to_name (TRUE, print != NULL, error); - - g_debug ("enroll_cb: result %s", name); - - if (print) - { - int r; - r = store.print_data_save (print); - if (r != 0) - name = "enroll-failed"; - } - - g_signal_emit (rdev, signals[SIGNAL_ENROLL_STATUS], 0, name, TRUE); - - if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("Device reported an error during enroll: %s", error->message); - - stoppable_action_completed (rdev); -} - -static void -enroll_start (FprintDevice *rdev) -{ - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - fp_device_enroll (priv->dev, - fprint_device_create_enroll_template (rdev, priv->enroll_data), - priv->current_cancellable, - enroll_progress_cb, - rdev, - NULL, - (GAsyncReadyCallback) enroll_cb, - rdev); -} - -static void -enroll_identify_cb (FpDevice *dev, GAsyncResult *res, void *user_data) -{ - g_autoptr(GError) error = NULL; - g_autoptr(FpPrint) matched_print = NULL; - g_autoptr(FpPrint) found_print = NULL; - FprintDevice *rdev = user_data; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - const char *name; - - fp_device_identify_finish (dev, res, &matched_print, &found_print, &error); - - if (g_error_matches (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_NOT_FOUND)) - { - g_clear_object (&found_print); - g_clear_error (&error); - } - - /* We may need to retry or error out. */ - if (error) - { - gboolean retry = error->domain == FP_DEVICE_RETRY; - - name = enroll_result_to_name (!retry, FALSE, error); - g_signal_emit (rdev, signals[SIGNAL_ENROLL_STATUS], 0, name, !retry); - - /* Retry or clean up. */ - if (retry) - { - g_autoptr(GPtrArray) all_prints = NULL; - - all_prints = load_all_prints (rdev); - fp_device_identify (priv->dev, - all_prints, - priv->current_cancellable, - NULL, - NULL, - NULL, - (GAsyncReadyCallback) enroll_identify_cb, - rdev); - } - else - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("Device reported an error during identify for enroll: %s", error->message); - - stoppable_action_completed (rdev); - } - - return; - } - - /* Identify has finished (successfully), there are three possible cases: - * 1. Match found in the gallery, in this case, we error out. - * 2. No match found, but on-device print returned, we should delete it - * 3. None of the above, we can just continue. - */ - - if (matched_print) - { - g_signal_emit (rdev, signals[SIGNAL_ENROLL_STATUS], 0, "enroll-duplicate", TRUE); - - stoppable_action_completed (rdev); - return; - } - - if (found_print && fp_device_has_feature (priv->dev, FP_DEVICE_FEATURE_STORAGE)) - { - if (!fp_print_get_device_stored (found_print)) - g_critical ("libfprint driver bug: Returned device print not marked as stored on device."); - - /* Try to delete the print (synchronously), and continue if it succeeds. */ - if (!fp_device_delete_print_sync (priv->dev, - found_print, - priv->current_cancellable, - &error)) - { - g_warning ("Failed to garbage collect duplicate print, cannot continue with enroll: %s", - error->message); - g_signal_emit (rdev, signals[SIGNAL_ENROLL_STATUS], 0, "enroll-duplicate", TRUE); - - stoppable_action_completed (rdev); - return; - } - } - - g_signal_emit (rdev, signals[SIGNAL_ENROLL_STATUS], 0, "enroll-stage-passed", FALSE); - - /* We are good and can start to enroll. */ - enroll_start (rdev); -} - -static gboolean -is_first_enrollment (FprintDevice * rdev) -{ - g_autoptr(GPtrArray) host_prints = NULL; - host_prints = load_all_prints (rdev); - - return host_prints->len == 0; -} - -static gboolean -fprint_device_enroll_start (FprintDBusDevice *dbus_dev, - GDBusMethodInvocation *invocation, - const char *finger_name) -{ - g_autoptr(GError) error = NULL; - g_autoptr(FpPrint) existing_print = NULL; - g_autoptr(SessionData) session = NULL; - FprintDevice *rdev = FPRINT_DEVICE (dbus_dev); - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - FpFinger finger = finger_name_to_fp_finger (finger_name); - - if (!_fprint_device_check_claimed (rdev, invocation, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - if (finger == FP_FINGER_UNKNOWN) - { - g_set_error (&error, FPRINT_ERROR, FPRINT_ERROR_INVALID_FINGERNAME, - "Invalid finger name"); - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - session = session_data_get (priv); - store.print_data_load (priv->dev, finger, - session->username, &existing_print); - - if (!can_start_action (rdev, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - if (existing_print) - { - if (!delete_enrolled_fingers (rdev, session->username, finger, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - } - - g_debug ("start enrollment device %d finger %d", priv->id, finger); - - priv->current_cancellable = g_cancellable_new (); - priv->enroll_data = finger; - priv->current_action = ACTION_ENROLL; - - if (!fp_device_has_feature (priv->dev, FP_DEVICE_FEATURE_STORAGE_LIST) && - is_first_enrollment (rdev)) - { - g_autoptr(GError) clear_err = NULL; - - if (!fp_device_clear_storage_sync (priv->dev, NULL, &clear_err)) - g_warning ("Failed to clear storage before first enrollment: %s", - clear_err->message); - } - - if (fp_device_has_feature (priv->dev, FP_DEVICE_FEATURE_IDENTIFY)) - { - g_autoptr(GPtrArray) all_prints = load_all_prints (rdev); - - /* We (now) have the policy that there must be no duplicate prints. - * We need to do this for MoC devices, as their "identify" function - * will generally just identify across all device stored prints. - * For MoH, we also do it. For consistency and because it allows us - * to implement new features in the future (i.e. logging in/unlocking - * the correct user without selecting it first). - */ - fp_device_identify (priv->dev, - all_prints, - priv->current_cancellable, - NULL, - NULL, - NULL, - (GAsyncReadyCallback) enroll_identify_cb, - rdev); - } - else - { - /* We may still want to try to use verification to check for duplicates - * if only one fingerprint was previously enrolled, or add more verify - * stages up to a predefined limit */ - g_warning ("Device %s does not support duplicate identification and so " - "fprintd duplicate detection won't work", - fp_device_get_name (priv->dev)); - enroll_start (rdev); - } - - fprint_dbus_device_complete_enroll_start (dbus_dev, invocation); - - return TRUE; -} - -static gboolean -fprint_device_enroll_stop (FprintDBusDevice *dbus_dev, - GDBusMethodInvocation *invocation) -{ - FprintDevice *rdev = FPRINT_DEVICE (dbus_dev); - - g_autoptr(GError) error = NULL; - - if (!_fprint_device_check_claimed (rdev, invocation, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - if (!can_stop_action (rdev, ACTION_ENROLL, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - stoppable_action_stop (rdev, invocation); - - return TRUE; -} - -static gboolean -fprint_device_list_enrolled_fingers (FprintDBusDevice *dbus_dev, - GDBusMethodInvocation *invocation, - const char *username) -{ - FprintDevice *rdev = FPRINT_DEVICE (dbus_dev); - - g_autoptr(GPtrArray) ret = NULL; - g_autoptr(GPtrArray) prints = NULL; - const char *sender; - const char *user; - guint i; - - sender = g_dbus_method_invocation_get_sender (invocation); - _fprint_device_add_client (rdev, sender); - - user = g_object_get_qdata (G_OBJECT (invocation), quark_auth_user); - g_assert (user); - prints = load_user_prints (rdev, user); - - ret = g_ptr_array_new (); - for (i = 0; i < prints->len; i++) - { - FpFinger finger = fp_print_get_finger (g_ptr_array_index (prints, i)); - - if (finger != FP_FINGER_UNKNOWN) - g_ptr_array_add (ret, (char *) fp_finger_to_name (finger)); - } - - if (!ret->len) - { - g_dbus_method_invocation_return_error_literal (invocation, - FPRINT_ERROR, - FPRINT_ERROR_NO_ENROLLED_PRINTS, - "Failed to discover prints"); - return TRUE; - } - - /* Add null-termination */ - g_ptr_array_add (ret, NULL); - - fprint_dbus_device_complete_list_enrolled_fingers (dbus_dev, - invocation, (const gchar *const *) ret->pdata); - - return TRUE; -} - -static gboolean -user_has_print_enrolled (FprintDevice *rdev, - const char *user, - FpFinger finger) -{ - g_autoptr(GSList) prints = NULL; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - prints = store.discover_prints (priv->dev, user); - - if (finger == FP_FINGER_UNKNOWN) - return prints != NULL; - - return g_slist_find (prints, GUINT_TO_POINTER (finger)) != NULL; -} - -static gboolean -delete_enrolled_fingers (FprintDevice *rdev, - const char *user, - FpFinger finger, - GError **error) -{ - g_autoptr(GError) device_error = NULL; - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - guint i; - int r; - - if (finger != FP_FINGER_UNKNOWN) - g_debug ("Deleting enrolled finger %s for user %s", - fp_finger_to_name (finger), user); - else - g_debug ("Deleting enrolled fingers for user %s", user); - - if (!user_has_print_enrolled (rdev, user, finger)) - { - if (finger != FP_FINGER_UNKNOWN) - g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_NO_ENROLLED_PRINTS, - "Fingerprint for finger %s is not enrolled", - fp_finger_to_name (finger)); - else - g_set_error_literal (error, FPRINT_ERROR, FPRINT_ERROR_NO_ENROLLED_PRINTS, - "No fingerprint enrolled"); - - return FALSE; - } - - /* First try deleting the print from the device, we don't consider it - * fatal if this does not work. */ - if (fp_device_has_feature (priv->dev, FP_DEVICE_FEATURE_STORAGE)) - { - g_autoptr(GSList) prints = NULL; - GSList *l; - - prints = store.discover_prints (priv->dev, user); - - for (l = prints; l != NULL; l = l->next) - { - g_autoptr(FpPrint) print = NULL; - - store.print_data_load (priv->dev, - GPOINTER_TO_UINT (l->data), - user, - &print); - - if (print) - { - g_autoptr(GError) local_error = NULL; - - if (finger != FP_FINGER_UNKNOWN && fp_print_get_finger (print) != finger) - continue; - - if (!fp_device_delete_print_sync (priv->dev, print, NULL, &local_error)) - { - g_warning ("Error deleting print from device: %s", local_error->message); - g_warning ("This might indicate an issue in the libfprint driver or in the fingerprint device."); - - if (!device_error) - { - g_set_error (&device_error, FPRINT_ERROR, - FPRINT_ERROR_PRINTS_NOT_DELETED_FROM_DEVICE, - "Failed to delete print from device storage: %s", - local_error->message); - } - } - } - } - } - - if (finger != FP_FINGER_UNKNOWN) - { - if ((r = store.print_data_delete (priv->dev, finger, user)) != 0) - { - if (user_has_print_enrolled (rdev, user, finger)) - { - g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_PRINTS_NOT_DELETED, - "Impossible to delete fingerprint reference " - "got error: %d", r); - return FALSE; - } - } - } - else - { - g_autoptr(GError) local_error = NULL; - - for (i = FP_FINGER_FIRST; i <= FP_FINGER_LAST; i++) - { - if ((r = store.print_data_delete (priv->dev, i, user)) != 0) - { - if (local_error) - continue; - - if (user_has_print_enrolled (rdev, user, i)) - { - g_set_error (&local_error, FPRINT_ERROR, FPRINT_ERROR_PRINTS_NOT_DELETED, - "Impossible to delete fingerprint reference " - "got error: %d", r); - /* Do not return yet, at least try to remove the remaining prints */ - } - } - } - - if (local_error) - { - g_propagate_error (error, g_steal_pointer (&local_error)); - return FALSE; - } - } - - if (device_error) - { - /* This is a low priority error, higher priority errors would have returned failure already */ - g_propagate_error (error, g_steal_pointer (&device_error)); - return FALSE; - } - - return TRUE; -} - -#ifdef __linux__ -static void -log_offending_client_cb (GObject *object, - GAsyncResult *res, - gpointer user_data) -{ - GDBusConnection *connection = G_DBUS_CONNECTION (object); - - g_autoptr(GVariant) ret = NULL; - g_autofree char *path = NULL; - g_autofree char *content = NULL; - guint pid = 0; - - ret = g_dbus_connection_call_finish (connection, res, NULL); - - if (!ret) - return; - - g_variant_get (ret, "(u)", &pid); - path = g_strdup_printf ("/proc/%u/comm", pid); - if (g_file_get_contents (path, &content, NULL, NULL)) - { - g_strchomp (content); - g_warning ("Offending API user is %s", content); - } -} - -static void -log_offending_client (GDBusMethodInvocation *invocation) -{ - const char *sender; - GDBusConnection *connection; - - connection = g_dbus_method_invocation_get_connection (invocation); - sender = g_dbus_method_invocation_get_sender (invocation); - - g_dbus_connection_call (connection, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetConnectionUnixProcessID", - g_variant_new ("(s)", sender), - NULL, G_DBUS_CALL_FLAGS_NONE, - -1, NULL, log_offending_client_cb, NULL); -} -#endif - -static gboolean -fprint_device_delete_enrolled_fingers (FprintDBusDevice *dbus_dev, - GDBusMethodInvocation *invocation, - const char *username) -{ - FprintDevice *rdev = FPRINT_DEVICE (dbus_dev); - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - g_autoptr(FprintDeviceActionUnset) action_unset = NULL; - g_autoptr(GError) error = NULL; - g_autofree char *user = NULL; - const char *sender; - gboolean opened; - - g_warning ("The API user should be updated to use DeleteEnrolledFingers2 method!"); -#ifdef __linux__ - log_offending_client (invocation); -#endif - - if (!can_start_action (rdev, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - priv->current_action = ACTION_DELETE; - action_unset = rdev; - - if (!_fprint_device_check_claimed (rdev, invocation, &error)) - { - /* Return error for anything but FPRINT_ERROR_CLAIM_DEVICE */ - if (!g_error_matches (error, FPRINT_ERROR, FPRINT_ERROR_CLAIM_DEVICE)) - { - g_dbus_method_invocation_return_gerror (invocation, - error); - return TRUE; - } - - opened = FALSE; - } - else - { - opened = fp_device_is_open (priv->dev); - } - - sender = g_dbus_method_invocation_get_sender (invocation); - _fprint_device_add_client (rdev, sender); - - if (!opened && fp_device_has_feature (priv->dev, FP_DEVICE_FEATURE_STORAGE)) - fp_device_open_sync (priv->dev, NULL, NULL); - - user = g_object_steal_qdata (G_OBJECT (invocation), quark_auth_user); - g_assert (user); - g_assert (g_str_equal (username, "") || g_str_equal (user, username)); - - g_clear_error (&error); - delete_enrolled_fingers (rdev, user, FP_FINGER_UNKNOWN, &error); - - if (!opened && fp_device_has_feature (priv->dev, FP_DEVICE_FEATURE_STORAGE)) - fp_device_close_sync (priv->dev, NULL, NULL); - - if (error) - { - g_dbus_method_invocation_return_gerror (invocation, - error); - return TRUE; - } - - fprint_dbus_device_complete_delete_enrolled_fingers (dbus_dev, - invocation); - return TRUE; -} - -static gboolean -fprint_device_delete_enrolled_fingers2 (FprintDBusDevice *dbus_dev, - GDBusMethodInvocation *invocation) -{ - FprintDevice *rdev = FPRINT_DEVICE (dbus_dev); - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - g_autoptr(SessionData) session = NULL; - g_autoptr(GError) error = NULL; - g_autoptr(FprintDeviceActionUnset) action_unset = NULL; - - if (!_fprint_device_check_claimed (rdev, invocation, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - if (!can_start_action (rdev, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - priv->current_action = ACTION_DELETE; - action_unset = rdev; - - session = session_data_get (priv); - - if (!delete_enrolled_fingers (rdev, session->username, FP_FINGER_UNKNOWN, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - fprint_dbus_device_complete_delete_enrolled_fingers2 (dbus_dev, - invocation); - return TRUE; -} - -static gboolean -fprint_device_delete_enrolled_finger (FprintDBusDevice *dbus_dev, - GDBusMethodInvocation *invocation, - const gchar *finger_name) -{ - FprintDevice *rdev = FPRINT_DEVICE (dbus_dev); - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - FpFinger finger = finger_name_to_fp_finger (finger_name); - - g_autoptr(FprintDeviceActionUnset) action_unset = NULL; - g_autoptr(SessionData) session = NULL; - g_autoptr(GError) error = NULL; - - if (!_fprint_device_check_claimed (rdev, invocation, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - if (finger == FP_FINGER_UNKNOWN) - { - g_dbus_method_invocation_return_error_literal (invocation, - FPRINT_ERROR, - FPRINT_ERROR_INVALID_FINGERNAME, - "Invalid finger name"); - return TRUE; - } - - if (!can_start_action (rdev, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - priv->current_action = ACTION_DELETE; - - session = session_data_get (priv); - action_unset = rdev; - - if (!delete_enrolled_fingers (rdev, session->username, finger, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - fprint_dbus_device_complete_delete_enrolled_finger (dbus_dev, invocation); - return TRUE; -} - -static gboolean -handle_unauthorized_access (FprintDevice *rdev, - GDBusMethodInvocation *invocation, - GError *error) -{ - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - - g_assert (error); - - g_warning ("Authorization denied to %s to call method '%s' for device '%s': %s", - g_dbus_method_invocation_get_sender (invocation), - g_dbus_method_invocation_get_method_name (invocation), - fp_device_get_name (priv->dev), - error->message); - g_dbus_method_invocation_return_gerror (invocation, error); - - return FALSE; -} - -static gboolean -action_authorization_handler (GDBusInterfaceSkeleton *interface, - GDBusMethodInvocation *invocation, - gpointer user_data) -{ - FprintDBusDevice *dbus_dev = FPRINT_DBUS_DEVICE (interface); - FprintDevice *rdev = FPRINT_DEVICE (dbus_dev); - FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); - FprintDevicePermission required_perms; - gboolean needs_user_auth = FALSE; - - g_autoptr(GError) error = NULL; - const gchar *method_name; - - method_name = g_dbus_method_invocation_get_method_name (invocation); - - g_debug ("Requesting authorization from %s to call method '%s' for device '%s'", - g_dbus_method_invocation_get_sender (invocation), method_name, - fp_device_get_name (priv->dev)); - - if (g_str_equal (method_name, "Claim")) - needs_user_auth = TRUE; - else if (g_str_equal (method_name, "DeleteEnrolledFingers")) - needs_user_auth = TRUE; - else if (g_str_equal (method_name, "ListEnrolledFingers")) - needs_user_auth = TRUE; - - /* This is just a quick check in order to avoid authentication if - * the user cannot make the call at this time anyway. - * The method handler itself is required to check again! */ - if (!_fprint_device_check_claimed (rdev, invocation, &error)) - return handle_unauthorized_access (rdev, invocation, error); - - if (needs_user_auth && - !fprintd_device_authorize_user (rdev, invocation, &error)) - return handle_unauthorized_access (rdev, invocation, error); - - required_perms = get_permissions_for_invocation (invocation); - - /* This may possibly block the invocation till the user has not - * provided an authentication method, so other calls could arrive */ - if (!fprint_device_check_polkit_for_permissions (rdev, invocation, - required_perms, - &error)) - return handle_unauthorized_access (rdev, invocation, error); - - g_debug ("Authorization granted to %s to call method '%s' for device '%s'!", - g_dbus_method_invocation_get_sender (invocation), - g_dbus_method_invocation_get_method_name (invocation), - fp_device_get_name (priv->dev)); - - return TRUE; -} - -static void -fprint_device_dbus_skeleton_iface_init (FprintDBusDeviceIface *iface) -{ - iface->handle_claim = fprint_device_claim; - iface->handle_delete_enrolled_finger = fprint_device_delete_enrolled_finger; - iface->handle_delete_enrolled_fingers = fprint_device_delete_enrolled_fingers; - iface->handle_delete_enrolled_fingers2 = fprint_device_delete_enrolled_fingers2; - iface->handle_enroll_start = fprint_device_enroll_start; - iface->handle_enroll_stop = fprint_device_enroll_stop; - iface->handle_list_enrolled_fingers = fprint_device_list_enrolled_fingers; - iface->handle_release = fprint_device_release; - iface->handle_verify_start = fprint_device_verify_start; - iface->handle_verify_stop = fprint_device_verify_stop; -} diff --git a/src/device.xml b/src/device.xml deleted file mode 100644 index a3c1dbe..0000000 --- a/src/device.xml +++ /dev/null @@ -1,645 +0,0 @@ - - - - - - - - - -]> - - - - - - PolicyKit integration - - - fprintd uses PolicyKit to check whether users are allowed to access fingerprint data, or the - fingerprint readers itself. - - - net.reactivated.fprint.device.verify - - Whether the user is allowed to verify fingers against saved fingerprints. - - - - net.reactivated.fprint.device.enroll - - Whether the user is allowed to enroll new fingerprints. - - - - net.reactivated.fprint.device.setusername - - Whether the user is allowed to query, verify, or enroll fingerprints for users other than itself. - - - - - - - Usernames - - - When a username argument is used for a method, a PolicyKit check is done on the - net.reactivated.fprint.device.setusername PolicyKit - action to see whether the user the client is running as is allowed to access data from other users. - - - By default, only root is allowed to access fingerprint data for users other than itself. For a normal user, - it is recommended that you use an empty string for the username, which will mean "the client the user is - running as". - - - See PolicyKit integration. - - - - Fingerprint names - - - When a finger name argument is used for a method, it refers to either a single finger, or - "any" finger. See the list of possible values below: - - - left-thumb - - Left thumb - - - - left-index-finger - - Left index finger - - - - left-middle-finger - - Left middle finger - - - - left-ring-finger - - Left ring finger - - - - left-little-finger - - Left little finger - - - - right-thumb - - Right thumb - - - - right-index-finger - - Right index finger - - - - right-middle-finger - - Right middle finger - - - - right-ring-finger - - Right ring finger - - - - right-little-finger - - Right little finger - - - - any - - Any finger. This is only used for Device.VerifyStart - (select the first finger with a fingerprint associated, or all the fingerprints available for the user when - the device supports it) and Device::VerifyFingerSelected - (any finger with an associated fingerprint can be used). - - - - - - - Verify Statuses - - - - Possible values for the result passed through Device::VerifyResult are: - - verify-no-match - - The verification did not match, Device.VerifyStop should now be called. - - - - verify-match - - The verification succeeded, Device.VerifyStop should now be called. - - - - verify-retry-scan - - The user should retry scanning their finger, the verification is still ongoing. - - - - verify-swipe-too-short - - The user's swipe was too short. The user should retry scanning their finger, the verification is still ongoing. - - - - verify-finger-not-centered - - The user's finger was not centered on the reader. The user should retry scanning their finger, the verification is still ongoing. - - - - verify-remove-and-retry - - The user should remove their finger from the reader and retry scanning their finger, the verification is still ongoing. - - - - verify-disconnected - - The device was disconnected during the verification, no other actions should be taken, and you shouldn't use the device any more. - - - - verify-unknown-error - - An unknown error occurred (usually a driver problem), Device.VerifyStop should now be called. - - - - - - - Enroll Statuses - - - - Possible values for the result passed through Device::EnrollResult are: - - enroll-completed - - The enrollment successfully completed, Device.EnrollStop should now be called. - - - - enroll-failed - - The enrollment failed, Device.EnrollStop should now be called. - - - - enroll-stage-passed - - One stage of the enrollment passed, the enrollment is still ongoing. - - - - enroll-retry-scan - - The user should retry scanning their finger, the enrollment is still ongoing. - - - - enroll-swipe-too-short - - The user's swipe was too short. The user should retry scanning their finger, the enrollment is still ongoing. - - - - enroll-finger-not-centered - - The user's finger was not centered on the reader. The user should retry scanning their finger, the enrollment is still ongoing. - - - - enroll-remove-and-retry - - The user should remove their finger from the reader and retry scanning their finger, the enrollment is still ongoing. - - - - enroll-data-full - - No further prints can be enrolled on this device, Device.EnrollStop should now be called. - - Delete other prints from the device first to continue - (e.g. from other users). Note that old prints or prints from other operating systems may be deleted automatically - to resolve this error without any notification. - - - - enroll-duplicate - - The print has already been enrolled, Device.EnrollStop should now be called. - - The user should enroll a different finger, or delete the print that has been enrolled already. - This print may be enrolled for a different user. - Note that an old duplicate (e.g. from a previous install) will be automatically garbage collected and should not cause any issues. - - - - enroll-disconnected - - The device was disconnected during the enrollment, no other actions should be taken, and you shouldn't use the device any more. - - - - - enroll-unknown-error - - An unknown error occurred (usually a driver problem), Device.EnrollStop should now be called. - - - - - - - - - - - - The username for whom to list the enrolled fingerprints. See Usernames. - - - An array of strings representing the enrolled fingerprints. See Fingerprint names. - - - - - List all the enrolled fingerprints for the chosen user. - - - - - if the caller lacks the appropriate PolicyKit authorization - if the chosen user doesn't have any fingerprints enrolled - - - - - - - - - The username for whom to delete the enrolled fingerprints. See Usernames. - - - - - Delete all the enrolled fingerprints for the chosen user. - - - This call only exists for compatibility reasons, you should instead claim the device using - Device.Claim and then call - DeleteEnrolledFingers2 or - DeleteEnrolledFinger. - - - - - if the caller lacks the appropriate PolicyKit authorization - if the fingerprint is not deleted from fprintd storage - - - - - - - - - - - Delete all the enrolled fingerprints for the user currently claiming the device with Device.Claim. - - - - - if the caller lacks the appropriate PolicyKit authorization - if the fingerprint is not deleted from fprintd storage - - - - - - - - - - A string representing the finger to delete. See - Fingerprint names. - Note that "any" is not a valid finger name for this method. - - - - - - Delete the enrolled fingerprint for the user currently claiming the device with Device.Claim. - - - - - if the caller lacks the appropriate PolicyKit authorization - if the device was not claimed - if the finger name passed is invalid - if the chosen user doesn't have the requsted fingerprint enrolled - if the fingerprint is not deleted from fprintd storage - - - - - - - - - The username for whom to claim the device. See Usernames. - - - - - Claim the device for the chosen user. - - - - - if the caller lacks the appropriate PolicyKit authorization - if the device is already claimed - if the device couldn't be claimed - - - - - - - - - - - Release a device claimed with Device.Claim. - - - - - if the caller lacks the appropriate PolicyKit authorization - if the device was not claimed - - - - - - - - - A string representing the finger to verify. See Fingerprint names. - - - - - Check the chosen finger against a saved fingerprint. You need to have claimed the device using - Device.Claim. The finger selected is sent to the front-end - using Device::VerifyFingerSelected and - verification status through Device::VerifyStatus. - - - - - if the caller lacks the appropriate PolicyKit authorization - if the device was not claimed - if the device was already being used - if there are no enrolled prints for the chosen user - if there was an internal error - - - - - - - - - - - Stop an on-going fingerprint verification started with Device.VerifyStart. - - - - - if the caller lacks the appropriate PolicyKit authorization - if the device was not claimed - if there was no ongoing verification - if there was an internal error - - - - - - - - - - - - A string representing the finger select to be verified. - - - - - - - Fingerprint names. - - - - - - - - - - - A string representing the status of the verification. - - - - - - - - Whether the verification finished and can be stopped. - - - - - - - Verify Statuses and Device.VerifyStop. - - - - - - - - - A string representing the finger to enroll. See - Fingerprint names. - Note that "any" is not a valid finger name for this method. - - - - - Start enrollment for the selected finger. You need to have claimed the device using - Device.Claim before calling - this method. Enrollment status is sent through Device::EnrollStatus. - - - - - if the caller lacks the appropriate PolicyKit authorization - if the device was not claimed - if the device was already being used - if the finger name passed is invalid - if the finger has been already enrolled by the user - if there was an internal error - - - - - - - - - - - - Stop an on-going fingerprint enrollment started with Device.EnrollStart. - - - - - if the caller lacks the appropriate PolicyKit authorization - if the device was not claimed - if there was no ongoing verification - if there was an internal error - - - - - - - - - - - A string representing the status of the enrollment. - - - - - - - - Whether the enrollment finished and can be stopped. - - - - - - - Enrollment Statuses and Device.EnrollStop. - - - - - - - - - - - The product name of the device. - - - - - - - - - - - - The number of enrollment stages for the device. This is only available when the device has been claimed, otherwise it will be undefined (-1). - - - Device.Claim and Device.EnrollStart. - - - - - - - - - - - - The scan type of the device, either "press" if you place your finger on the device, or "swipe" if you have to swipe your finger. - - - - - - - - - - - - Whether the finger is on sensor. - - - - - - - - - - - - Whether the sensor is waiting for the finger. - - - - - - - - diff --git a/src/file_storage.c b/src/file_storage.c deleted file mode 100644 index e12fe58..0000000 --- a/src/file_storage.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Simple file storage for fprintd - * Copyright (C) 2007 Daniel Drake - * Copyright (C) 2008 Vasily Khoruzhick - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - - -/* FIXME: - * This file almost duplicate data.c from libfprint - * Maybe someday data.c will be upgraded to this one ;) - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "file_storage.h" - -#define FILE_STORAGE_PATH "/var/lib/fprint" -#define DIR_PERMS 0700 - -static char *storage_path = NULL; - -static const char * -get_storage_path (void) -{ - const char *path = NULL; - - if (storage_path != NULL) - return storage_path; - - /* set by systemd >= 240 to an absolute path - * taking into account the StateDirectory - * unit file setting */ - path = g_getenv ("STATE_DIRECTORY"); - if (path != NULL) - { - /* If multiple directories are set, then in the environment variable - * the paths are concatenated with colon (":"). */ - if (strchr (path, ':')) - { - g_auto(GStrv) elems = NULL; - elems = g_strsplit (path, ":", -1); - storage_path = g_strdup (elems[0]); - } - else if (*path) - { - storage_path = g_strdup (path); - } - } - - if (storage_path == NULL) - storage_path = g_strdup (FILE_STORAGE_PATH); - - return storage_path; -} - -static char * -get_path_to_storedir (const char *driver, const char * device_id, char *base_store) -{ - return g_build_filename (base_store, driver, device_id, NULL); -} - -static char * -__get_path_to_print (const char *driver, const char * device_id, - FpFinger finger, char *base_store) -{ - g_autofree char *dirpath = NULL; - char *path; - char fingername[2]; - - g_snprintf (fingername, 2, "%x", finger); - - dirpath = get_path_to_storedir (driver, device_id, base_store); - path = g_build_filename (dirpath, fingername, NULL); - return path; -} - -static char * -get_path_to_print (FpDevice *dev, FpFinger finger, char *base_store) -{ - return __get_path_to_print (fp_device_get_driver (dev), - fp_device_get_device_id (dev), - finger, - base_store); -} - -static char * -get_path_to_print_dscv (FpDevice *dev, FpFinger finger, char *base_store) -{ - return __get_path_to_print (fp_device_get_driver (dev), - fp_device_get_device_id (dev), - finger, - base_store); -} - -static char * -file_storage_get_basestore_for_username (const char *username) -{ - return g_build_filename (get_storage_path (), username, NULL); -} - -int -file_storage_print_data_save (FpPrint *print) -{ - g_autoptr(GError) err = NULL; - g_autofree char *path = NULL; - g_autofree char *dirpath = NULL; - g_autofree char *base_store = NULL; - g_autofree char *buf = NULL; - gsize len; - int r; - - base_store = file_storage_get_basestore_for_username (fp_print_get_username (print)); - - if (!fp_print_serialize (print, (guchar **) &buf, &len, &err)) - { - g_warning ("Error serializing data: %s", err->message); - return -ENOMEM; - } - - path = __get_path_to_print (fp_print_get_driver (print), - fp_print_get_device_id (print), - fp_print_get_finger (print), - base_store); - dirpath = g_path_get_dirname (path); - r = g_mkdir_with_parents (dirpath, DIR_PERMS); - if (r < 0) - { - g_debug ("file_storage_print_data_save(): could not mkdir(\"%s\"): %s", - dirpath, g_strerror (r)); - return r; - } - - g_file_set_contents (path, buf, len, &err); - if (err) - { - g_debug ("file_storage_print_data_save(): could not save '%s': %s", - path, err->message); - /* FIXME interpret error codes */ - return err->code; - } - - g_debug ("file_storage_print_data_save(): print saved to %s", path); - - return 0; -} - -static int -load_from_file (char *path, FpPrint **print) -{ - g_autoptr(GError) err = NULL; - gsize length; - g_autofree char *contents = NULL; - FpPrint *new; - - g_file_get_contents (path, &contents, &length, &err); - if (err) - { - int r = err->code; - /* FIXME interpret more error codes */ - if (r == G_FILE_ERROR_NOENT) - return -ENOENT; - else - return r; - } - - new = fp_print_deserialize ((guchar *) contents, length, &err); - if (!new) - { - g_print ("Error deserializing data: %s", err->message); - return -EIO; - } - - *print = new; - return 0; -} - -int -file_storage_print_data_load (FpDevice *dev, - FpFinger finger, - const char *username, - FpPrint **print) -{ - g_autofree gchar *path = NULL; - g_autofree gchar *base_store = NULL; - - g_autoptr(FpPrint) new = NULL; - int r; - - base_store = file_storage_get_basestore_for_username (username); - - path = get_path_to_print (dev, finger, base_store); - r = load_from_file (path, &new); - g_debug ("file_storage_print_data_load(): loaded '%s' %s", - path, g_strerror (r)); - if (r) - return r; - - /* Make sure the username/finger matches our expectations. */ - if (fp_print_get_finger (new) != finger) - return -EINVAL; - - if (g_strcmp0 (fp_print_get_username (new), username) != 0) - return -EINVAL; - - /* And that the print is compatible with the device. */ - if (!fp_print_compatible (new, dev)) - return -EINVAL; - - *print = g_steal_pointer (&new); - return 0; -} - -int -file_storage_print_data_delete (FpDevice *dev, FpFinger finger, const char *username) -{ - g_autoptr(GSList) prints = NULL; - g_autofree gchar *base_store = NULL; - g_autofree gchar *path = NULL; - int r; - - base_store = file_storage_get_basestore_for_username (username); - - path = get_path_to_print_dscv (dev, finger, base_store); - - if (!g_file_test (path, G_FILE_TEST_EXISTS)) - return 0; - - r = g_unlink (path); - g_debug ("file_storage_print_data_delete(): unlink(\"%s\") %s", - path, g_strerror (r)); - - prints = file_storage_discover_prints (dev, username); - if (!prints) - { - g_autofree char *dir = g_steal_pointer (&path); - - do - { - g_autofree char *tmp = g_steal_pointer (&dir); - dir = g_path_get_dirname (tmp); - } - while (g_str_has_prefix (dir, base_store) && g_rmdir (dir) == 0); - } - - return r; -} - -static GSList * -scan_dev_storedir (char *devpath, - GSList *list) -{ - g_autoptr(GError) err = NULL; - const gchar *ent; - - GDir *dir = g_dir_open (devpath, 0, &err); - - if (!dir) - { - g_debug ("scan_dev_storedir(): opendir(\"%s\") failed: %s", devpath, err->message); - return list; - } - - while ((ent = g_dir_read_name (dir))) - { - /* ent is an 1 hex character fp_finger code */ - guint64 val; - gchar *endptr; - - if (*ent == 0 || strlen (ent) != 1) - continue; - - val = g_ascii_strtoull (ent, &endptr, 16); - if (endptr == ent || !FP_FINGER_IS_VALID (val)) - { - g_debug ("scan_dev_storedir(): skipping print file '%s'", ent); - continue; - } - - list = g_slist_prepend (list, GUINT_TO_POINTER (val)); - } - - g_dir_close (dir); - return list; -} - -GSList * -file_storage_discover_prints (FpDevice *dev, const char *username) -{ - GSList *list = NULL; - g_autofree gchar *base_store = NULL; - g_autofree gchar *storedir = NULL; - - base_store = file_storage_get_basestore_for_username (username); - - storedir = get_path_to_storedir (fp_device_get_driver (dev), - fp_device_get_device_id (dev), - base_store); - - g_debug ("file_storage_discover_prints() for user '%s' in '%s'", - username, storedir); - - list = scan_dev_storedir (storedir, list); - - return list; -} - -GSList * -file_storage_discover_users (void) -{ - g_autoptr(GError) err = NULL; - GSList *list = NULL; - const gchar *ent; - GDir *dir = g_dir_open (get_storage_path (), 0, &err); - - if (!dir) - return list; - - while ((ent = g_dir_read_name (dir))) - { - /* ent is a username */ - if (*ent == 0) - continue; - - list = g_slist_prepend (list, g_strdup (ent)); - } - - g_dir_close (dir); - return list; -} - -int -file_storage_init (void) -{ - /* Nothing to do */ - return 0; -} - -int -file_storage_deinit (void) -{ - g_clear_pointer (&storage_path, g_free); - return 0; -} diff --git a/src/file_storage.h b/src/file_storage.h deleted file mode 100644 index 29e70df..0000000 --- a/src/file_storage.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Simple file storage for fprintd - * Copyright (C) 2008 Vasily Khoruzhick - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#pragma once - -int file_storage_print_data_save (FpPrint *print); - -int file_storage_print_data_load (FpDevice *dev, - FpFinger finger, - const char *username, - FpPrint **print); - -int file_storage_print_data_delete (FpDevice *dev, - FpFinger finger, - const char *username); - -int file_storage_init (void); - -int file_storage_deinit (void); - -GSList *file_storage_discover_prints (FpDevice *dev, - const char *username); -GSList *file_storage_discover_users (void); diff --git a/src/fprintd.h b/src/fprintd.h deleted file mode 100644 index d414e68..0000000 --- a/src/fprintd.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * fprintd header file - * Copyright (C) 2008 Daniel Drake - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#pragma once - -#include -#include -#include -#include "fprintd-enums.h" -#include "fprintd-dbus.h" - -/* General */ -#define TIMEOUT 30 -#define FPRINT_SERVICE_NAME "net.reactivated.Fprint" -#define FPRINT_SERVICE_PATH "/net/reactivated/Fprint" - -/* Errors */ -GQuark fprint_error_quark (void); - -#define FPRINT_ERROR fprint_error_quark () -typedef enum { - /* developer didn't claim the device */ - FPRINT_ERROR_CLAIM_DEVICE, /*< nick=net.reactivated.Fprint.Error.ClaimDevice >*/ - /* device is already claimed by somebody else */ - FPRINT_ERROR_ALREADY_IN_USE, /*< nick=net.reactivated.Fprint.Error.AlreadyInUse >*/ - /* internal error occurred */ - FPRINT_ERROR_INTERNAL, /*< nick=net.reactivated.Fprint.Error.Internal >*/ - /* PolicyKit refused the action */ - FPRINT_ERROR_PERMISSION_DENIED, /*< nick=net.reactivated.Fprint.Error.PermissionDenied >*/ - /* No prints are enrolled */ - FPRINT_ERROR_NO_ENROLLED_PRINTS, /*< nick=net.reactivated.Fprint.Error.NoEnrolledPrints >*/ - /* Prints has already been enrolled */ - FPRINT_ERROR_FINGER_ALREADY_ENROLLED, /*< nick=net.reactivated.Fprint.Error.FingerAlreadyEnrolled >*/ - /* No actions currently in progress */ - FPRINT_ERROR_NO_ACTION_IN_PROGRESS, /*< nick=net.reactivated.Fprint.Error.NoActionInProgress >*/ - /* the finger name passed was invalid */ - FPRINT_ERROR_INVALID_FINGERNAME, /*< nick=net.reactivated.Fprint.Error.InvalidFingername >*/ - /* device does not exist */ - FPRINT_ERROR_NO_SUCH_DEVICE, /*< nick=net.reactivated.Fprint.Error.NoSuchDevice >*/ - /* Prints cannot be deleted from the fprintd storage */ - FPRINT_ERROR_PRINTS_NOT_DELETED, /*< nick=net.reactivated.Fprint.Error.PrintsNotDeleted >*/ - /* Prints cannot be deleted from the device storage */ - FPRINT_ERROR_PRINTS_NOT_DELETED_FROM_DEVICE, /*< nick=net.reactivated.Fprint.Error.PrintsNotDeletedFromDevice >*/ -} FprintError; - -/* Enum of possible permissions, orders and nick matter here: - - The order controls the priority of a required permission when various are - accepted: the lowest the value, the more priority it has. - - Nick must match the relative polkit rule. - */ -typedef enum { - FPRINT_DEVICE_PERMISSION_NONE = 0, - FPRINT_DEVICE_PERMISSION_VERIFY = (1 << 0), /*< nick=net.reactivated.fprint.device.verify >*/ - FPRINT_DEVICE_PERMISSION_ENROLL = (1 << 1), /*< nick=net.reactivated.fprint.device.enroll >*/ - FPRINT_DEVICE_PERMISSION_SETUSERNAME = (1 << 2), /*< nick=net.reactivated.fprint.device.setusername >*/ -} FprintDevicePermission; - -/* Manager */ -#define FPRINT_TYPE_MANAGER (fprint_manager_get_type ()) -G_DECLARE_FINAL_TYPE (FprintManager, fprint_manager, FPRINT, MANAGER, GObject) - -struct _FprintManager -{ - GObject parent; -}; - -FprintManager *fprint_manager_new (GDBusConnection *connection, - gboolean no_timeout); - -/* Device */ -#define FPRINT_TYPE_DEVICE (fprint_device_get_type ()) -G_DECLARE_FINAL_TYPE (FprintDevice, fprint_device, FPRINT, DEVICE, - FprintDBusDeviceSkeleton) - -struct _FprintDevice -{ - FprintDBusDeviceSkeleton parent; -}; - -FprintDevice *fprint_device_new (FpDevice *dev); -guint32 _fprint_device_get_id (FprintDevice *rdev); - -void fprint_device_suspend (FprintDevice *rdev, - GAsyncReadyCallback callback, - void *user_data); -void fprint_device_resume (FprintDevice *rdev, - GAsyncReadyCallback callback, - void *user_data); - -void fprint_device_suspend_finish (FprintDevice *rdev, - GAsyncResult *result, - GError **error); -void fprint_device_resume_finish (FprintDevice *rdev, - GAsyncResult *res, - GError **error); - - -/* Print */ -/* TODO */ - - -/* Some compatibility definitions for older GLib. Copied from from libfprint. */ -#if !GLIB_CHECK_VERSION (2, 57, 0) -G_DEFINE_AUTOPTR_CLEANUP_FUNC (GTypeClass, g_type_class_unref); -G_DEFINE_AUTOPTR_CLEANUP_FUNC (GEnumClass, g_type_class_unref); -G_DEFINE_AUTOPTR_CLEANUP_FUNC (GFlagsClass, g_type_class_unref); -G_DEFINE_AUTOPTR_CLEANUP_FUNC (GParamSpec, g_param_spec_unref); -#else -/* Re-define G_SOURCE_FUNC as we are technically not allowed to use it with - * the version we depend on currently. */ -#undef G_SOURCE_FUNC -#endif - -#define G_SOURCE_FUNC(f) ((GSourceFunc) (void (*)(void))(f)) diff --git a/src/main.c b/src/main.c deleted file mode 100644 index f2328da..0000000 --- a/src/main.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * fprint D-Bus daemon - * Copyright (C) 2008 Daniel Drake - * Copyright (C) 2020 Marco Trevisan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "fprintd.h" -#include "storage.h" -#include "file_storage.h" - -fp_storage store; - -static gboolean no_timeout = FALSE; -static gboolean g_fatal_warnings = FALSE; - -static void -set_storage_file (void) -{ - store.init = &file_storage_init; - store.deinit = &file_storage_deinit; - store.print_data_save = &file_storage_print_data_save; - store.print_data_load = &file_storage_print_data_load; - store.print_data_delete = &file_storage_print_data_delete; - store.discover_prints = &file_storage_discover_prints; - store.discover_users = &file_storage_discover_users; -} - -static gboolean -load_storage_module (const char *module_name) -{ - GModule *module; - g_autofree char *filename = NULL; - - filename = g_module_build_path (PLUGINDIR, module_name); - module = g_module_open (filename, 0); - if (module == NULL) - return FALSE; - - if (!g_module_symbol (module, "init", (gpointer *) &store.init) || - !g_module_symbol (module, "deinit", (gpointer *) &store.deinit) || - !g_module_symbol (module, "print_data_save", (gpointer *) &store.print_data_save) || - !g_module_symbol (module, "print_data_load", (gpointer *) &store.print_data_load) || - !g_module_symbol (module, "print_data_delete", (gpointer *) &store.print_data_delete) || - !g_module_symbol (module, "discover_prints", (gpointer *) &store.discover_prints)) - { - g_module_close (module); - return FALSE; - } - - g_module_make_resident (module); - - return TRUE; -} - -static gboolean -load_conf (void) -{ - g_autofree char *filename = NULL; - g_autofree char *module_name = NULL; - - g_autoptr(GKeyFile) file = NULL; - g_autoptr(GError) error = NULL; - - filename = g_build_filename (SYSCONFDIR, "fprintd.conf", NULL); - file = g_key_file_new (); - g_debug ("About to load configuration file '%s'", filename); - if (!g_key_file_load_from_file (file, filename, G_KEY_FILE_NONE, &error)) - { - g_warning ("Could not open \"%s\": %s\n", filename, error->message); - return FALSE; - } - - module_name = g_key_file_get_string (file, "storage", "type", &error); - if (module_name == NULL) - return FALSE; - - if (g_str_equal (module_name, "file")) - { - set_storage_file (); - return TRUE; - } - - return load_storage_module (module_name); -} - -static const GOptionEntry entries[] = { - {"g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &g_fatal_warnings, "Make all warnings fatal", NULL}, - {"no-timeout", 't', 0, G_OPTION_ARG_NONE, &no_timeout, "Do not exit after unused for a while", NULL}, - { NULL } -}; - -static gboolean -sigterm_callback (gpointer data) -{ - GMainLoop *loop = data; - - g_main_loop_quit (loop); - return FALSE; -} - -static void -on_name_acquired (GDBusConnection *connection, - const char *name, - gpointer user_data) -{ - g_debug ("D-Bus service launched with name: %s", name); -} - -static void -on_name_lost (GDBusConnection *connection, - const char *name, - gpointer user_data) -{ - GMainLoop *loop = user_data; - - g_warning ("Failed to get name: %s", name); - - g_main_loop_quit (loop); -} - -int -main (int argc, char **argv) -{ - g_autoptr(GOptionContext) context = NULL; - g_autoptr(GMainLoop) loop = NULL; - g_autoptr(GError) error = NULL; - g_autoptr(FprintManager) manager = NULL; - g_autoptr(GDBusConnection) connection = NULL; - guint32 request_name_ret; - - setlocale (LC_ALL, ""); - - bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - textdomain (GETTEXT_PACKAGE); - - context = g_option_context_new ("Fingerprint handler daemon"); - g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); - - if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) - { - g_warning ("couldn't parse command-line options: %s\n", error->message); - return 1; - } - - if (g_fatal_warnings) - { - GLogLevelFlags fatal_mask; - - fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK); - fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL; - g_log_set_always_fatal (fatal_mask); - } - - /* Obtain a connection to the system bus */ - connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); - if (!G_IS_DBUS_CONNECTION (connection)) - { - g_warning ("Failed to open connection to bus: %s", error->message); - return 1; - } - - /* Load the configuration file, - * and the default storage plugin */ - if (!load_conf ()) - set_storage_file (); - store.init (); - - loop = g_main_loop_new (NULL, FALSE); - g_unix_signal_add (SIGTERM, sigterm_callback, loop); - - g_debug ("Launching FprintObject"); - - /* create the one instance of the Manager object to be shared between - * all fprintd users. This blocks until all the devices are enumerated */ - manager = fprint_manager_new (connection, no_timeout); - - /* Obtain the well-known name after the manager has been initialized. - * Otherwise a client immediately enumerating the devices will not see - * any. */ - request_name_ret = g_bus_own_name_on_connection (connection, - FPRINT_SERVICE_NAME, - G_BUS_NAME_OWNER_FLAGS_NONE, - on_name_acquired, - on_name_lost, - loop, NULL); - - g_debug ("entering main loop"); - g_main_loop_run (loop); - g_bus_unown_name (request_name_ret); - g_debug ("main loop completed"); - - store.deinit (); - - return 0; -} diff --git a/src/manager.c b/src/manager.c deleted file mode 100644 index a3dae5d..0000000 --- a/src/manager.c +++ /dev/null @@ -1,631 +0,0 @@ -/* - * /net/reactivated/Fprint/Manager object implementation - * Copyright (C) 2008 Daniel Drake - * Copyright (C) 2020 Marco Trevisan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "fprintd.h" - -#define LOGIND_BUS_NAME "org.freedesktop.login1" -#define LOGIND_IFACE_NAME "org.freedesktop.login1.Manager" -#define LOGIND_OBJ_PATH "/org/freedesktop/login1" - -static void fprint_manager_constructed (GObject *object); -static gboolean fprint_manager_get_devices (FprintManager *manager, - GPtrArray **devices, - GError **error); -static gboolean fprint_manager_get_default_device (FprintManager *manager, - const char **device, - GError **error); - -typedef struct -{ - GDBusConnection *connection; - GDBusObjectManager *object_manager; - FprintDBusManager *dbus_manager; - FpContext *context; - gboolean no_timeout; - guint timeout_id; - gint prepare_for_sleep_pending; - guint prepare_for_sleep_id; - gint sleep_inhibit_fd; -} FprintManagerPrivate; - -G_DEFINE_TYPE_WITH_CODE (FprintManager, fprint_manager, G_TYPE_OBJECT, G_ADD_PRIVATE (FprintManager)) - -enum { - PROP_0, - FPRINT_MANAGER_CONNECTION, - N_PROPS -}; - -static GParamSpec *properties[N_PROPS]; - -static void -fprint_manager_finalize (GObject *object) -{ - FprintManagerPrivate *priv = fprint_manager_get_instance_private (FPRINT_MANAGER (object)); - - if (priv->prepare_for_sleep_id) - g_dbus_connection_signal_unsubscribe (priv->connection, - priv->prepare_for_sleep_id); - - g_clear_object (&priv->object_manager); - g_clear_object (&priv->dbus_manager); - g_clear_object (&priv->connection); - g_clear_object (&priv->context); - - G_OBJECT_CLASS (fprint_manager_parent_class)->finalize (object); -} - -static FprintDevice * -fprint_dbus_object_skeleton_get_device (FprintDBusObjectSkeleton *object) -{ - FprintDevice *rdev; - - g_object_get (object, "device", &rdev, NULL); - return rdev; -} - -static void -fprint_manager_set_property (GObject *object, guint property_id, - const GValue *value, GParamSpec *pspec) -{ - FprintManager *self = FPRINT_MANAGER (object); - FprintManagerPrivate *priv = fprint_manager_get_instance_private (self); - - switch (property_id) - { - case FPRINT_MANAGER_CONNECTION: - priv->connection = g_value_dup_object (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -fprint_manager_get_property (GObject *object, guint property_id, - GValue *value, GParamSpec *pspec) -{ - FprintManager *self = FPRINT_MANAGER (object); - FprintManagerPrivate *priv = fprint_manager_get_instance_private (self); - - switch (property_id) - { - case FPRINT_MANAGER_CONNECTION: - g_value_set_object (value, priv->connection); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -fprint_manager_class_init (FprintManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->constructed = fprint_manager_constructed; - object_class->set_property = fprint_manager_set_property; - object_class->get_property = fprint_manager_get_property; - object_class->finalize = fprint_manager_finalize; - - properties[FPRINT_MANAGER_CONNECTION] = - g_param_spec_object ("connection", - "Connection", - "Set GDBus connection property", - G_TYPE_DBUS_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static gchar * -get_device_path (FprintDevice *rdev) -{ - return g_strdup_printf (FPRINT_SERVICE_PATH "/Device/%d", - _fprint_device_get_id (rdev)); -} - -static gboolean -fprint_manager_timeout_cb (FprintManager *manager) -{ - //FIXME kill all the devices - exit (0); - return FALSE; -} - -static void -fprint_manager_busy_notified (FprintDevice *rdev, GParamSpec *spec, FprintManager *manager) -{ - FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager); - guint num_devices_busy = 0; - - g_autolist (GDBusObject) devices = NULL; - GList *l; - gboolean busy; - - if (priv->timeout_id > 0) - { - g_source_remove (priv->timeout_id); - priv->timeout_id = 0; - } - if (priv->no_timeout) - return; - - devices = g_dbus_object_manager_get_objects (priv->object_manager); - - for (l = devices; l != NULL; l = l->next) - { - g_autoptr(FprintDevice) dev = NULL; - FprintDBusObjectSkeleton *object = l->data; - - dev = fprint_dbus_object_skeleton_get_device (object); - g_object_get (G_OBJECT (dev), "busy", &busy, NULL); - if (busy != FALSE) - num_devices_busy++; - } - - if (num_devices_busy == 0) - priv->timeout_id = g_timeout_add_seconds (TIMEOUT, (GSourceFunc) fprint_manager_timeout_cb, manager); -} - -static gboolean -handle_get_devices (FprintManager *manager, GDBusMethodInvocation *invocation, - FprintDBusManager *skeleton) -{ - g_autoptr(GPtrArray) devices = NULL; - g_autoptr(GError) error = NULL; - - if (!fprint_manager_get_devices (manager, &devices, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - fprint_dbus_manager_complete_get_devices (skeleton, invocation, - (const gchar *const *) - devices->pdata); - - return TRUE; -} - -static gboolean -handle_get_default_device (FprintManager *manager, - GDBusMethodInvocation *invocation, - FprintDBusManager *skeleton) -{ - const gchar *device; - - g_autoptr(GError) error = NULL; - - if (!fprint_manager_get_default_device (manager, &device, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return TRUE; - } - - fprint_dbus_manager_complete_get_default_device (skeleton, invocation, - device); - - return TRUE; -} - -static void -fprint_device_suspend_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - g_autoptr(GError) error = NULL; - FprintManager *manager = FPRINT_MANAGER (user_data); - FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager); - - /* Fetch the result (except for the NULL dummy call). */ - if (source_object != NULL) - { - fprint_device_suspend_finish (FPRINT_DEVICE (source_object), res, &error); - if (error) - { - if (!g_error_matches (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_NOT_OPEN) && - !g_error_matches (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_NOT_SUPPORTED)) - g_message ("Unexpected error while suspending device: %s", error->message); - } - } - - priv->prepare_for_sleep_pending -= 1; - - /* Close FD when all devices are prepared for sleeping. */ - if (priv->prepare_for_sleep_pending == 0) - { - if (priv->sleep_inhibit_fd >= 0) - close (priv->sleep_inhibit_fd); - priv->sleep_inhibit_fd = -1; - g_debug ("Released delay inhibitor for sleep."); - } - - g_object_unref (manager); -} - -static void -logind_sleep_inhibit_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - g_autoptr(GVariant) data = NULL; - g_autoptr(GError) error = NULL; - g_autoptr(GUnixFDList) out_fd_list = NULL; - g_autoptr(FprintManager) manager = FPRINT_MANAGER (user_data); - FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager); - gint fd_offset; - - data = g_dbus_connection_call_with_unix_fd_list_finish (priv->connection, &out_fd_list, res, &error); - - if (!data) - { - g_warning ("Failed to install a sleep delay inhibitor: %s", error->message); - return; - } - - if (priv->sleep_inhibit_fd >= 0) - close (priv->sleep_inhibit_fd); - - g_debug ("Got delay inhibitor for sleep."); - - g_variant_get (data, "(h)", &fd_offset); - priv->sleep_inhibit_fd = g_unix_fd_list_get (out_fd_list, fd_offset, NULL); -} - -static void -handle_prepare_for_sleep_signal (GDBusConnection *connection, - const gchar *sender_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *signal_name, - GVariant *parameters, - gpointer user_data) -{ - g_autolist (GDBusObject) devices = NULL; - FprintManager *manager = FPRINT_MANAGER (user_data); - FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager); - gboolean prepare_for_sleep; - GList *l; - - if (!g_variant_check_format_string (parameters, "(b)", FALSE)) - { - g_warning ("Received incorrect parameter for PrepareForSleep signal"); - return; - } - - g_variant_get (parameters, "(b)", &prepare_for_sleep); - - /* called one more time to handle the case of no devices */ - if (prepare_for_sleep) - priv->prepare_for_sleep_pending = 1; - - devices = g_dbus_object_manager_get_objects (priv->object_manager); - - g_debug ("Preparing devices for %s", prepare_for_sleep ? "sleep" : "resume"); - - for (l = devices; l != NULL; l = l->next) - { - g_autoptr(FprintDevice) dev = NULL; - FprintDBusObjectSkeleton *object = l->data; - - dev = fprint_dbus_object_skeleton_get_device (object); - - if (prepare_for_sleep) - { - priv->prepare_for_sleep_pending += 1; - g_object_ref (manager); - fprint_device_suspend (dev, fprint_device_suspend_cb, manager); - } - else - { - fprint_device_resume (dev, NULL, NULL); - } - } - - if (prepare_for_sleep) - { - /* "Notify" the initial dummy device we added, handling no devices that suspending */ - g_object_ref (manager); - fprint_device_suspend_cb (NULL, NULL, manager); - } - else - { - GVariant *arg = NULL; - - arg = g_variant_new ("(ssss)", - "sleep", - "net.reactivated.Fprint", - "Suspend fingerprint readers", - "delay"); - - /* Grab a sleep inhibitor. */ - g_dbus_connection_call_with_unix_fd_list (priv->connection, - LOGIND_BUS_NAME, - LOGIND_OBJ_PATH, - LOGIND_IFACE_NAME, - "Inhibit", - arg, - G_VARIANT_TYPE ("(h)"), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - NULL, - logind_sleep_inhibit_cb, - g_object_ref (manager)); - } -} - -static void -device_added_cb (FprintManager *manager, FpDevice *device, FpContext *context) -{ - FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager); - - g_autoptr(FprintDBusObjectSkeleton) object = NULL; - g_autoptr(FprintDevice) rdev = NULL; - g_autofree gchar *path = NULL; - - rdev = fprint_device_new (device); - - g_signal_connect (G_OBJECT (rdev), "notify::busy", - G_CALLBACK (fprint_manager_busy_notified), manager); - - path = get_device_path (rdev); - - object = fprint_dbus_object_skeleton_new (path); - fprint_dbus_object_skeleton_set_device (object, - FPRINT_DBUS_DEVICE (rdev)); - g_dbus_object_manager_server_export ( - G_DBUS_OBJECT_MANAGER_SERVER (priv->object_manager), - G_DBUS_OBJECT_SKELETON (object)); -} - -static void -device_removed_cb (FprintManager *manager, FpDevice *device, FpContext *context) -{ - FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager); - - g_autolist (FprintDBusObjectSkeleton) objects = NULL; - GList *item; - - objects = g_dbus_object_manager_get_objects (priv->object_manager); - - for (item = objects; item; item = item->next) - { - g_autoptr(FprintDevice) rdev = NULL; - g_autoptr(FpDevice) dev = NULL; - FprintDBusObjectSkeleton *object = item->data; - - rdev = fprint_dbus_object_skeleton_get_device (object); - g_object_get (rdev, "dev", &dev, NULL); - if (dev != device) - continue; - - g_dbus_object_manager_server_unexport ( - G_DBUS_OBJECT_MANAGER_SERVER (priv->object_manager), - g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (rdev))); - - g_signal_handlers_disconnect_by_data (rdev, manager); - - /* We cannot continue to iterate at this point, but we don't need to either */ - break; - } - - /* The device that disappeared might have been busy. - * Do we need to do anything else in this case to clean up more gracefully? */ - fprint_manager_busy_notified (NULL, NULL, manager); -} - -static void -fprint_manager_constructed (GObject *object) -{ - g_autoptr(GVariant) param_false = NULL; - FprintManager *manager = FPRINT_MANAGER (object); - FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager); - GDBusObjectManagerServer *object_manager_server; - - object_manager_server = - g_dbus_object_manager_server_new (FPRINT_SERVICE_PATH); - - priv->object_manager = G_DBUS_OBJECT_MANAGER (object_manager_server); - priv->dbus_manager = fprint_dbus_manager_skeleton_new (); - priv->context = fp_context_new (); - - g_signal_connect_object (priv->dbus_manager, - "handle-get-devices", - G_CALLBACK (handle_get_devices), - manager, - G_CONNECT_SWAPPED); - g_signal_connect_object (priv->dbus_manager, - "handle-get-default-device", - G_CALLBACK (handle_get_default_device), - manager, - G_CONNECT_SWAPPED); - - g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->dbus_manager), - priv->connection, - FPRINT_SERVICE_PATH "/Manager", NULL); - - g_dbus_object_manager_server_set_connection (object_manager_server, - priv->connection); - - priv->prepare_for_sleep_id = g_dbus_connection_signal_subscribe (priv->connection, - LOGIND_BUS_NAME, - LOGIND_IFACE_NAME, - "PrepareForSleep", - LOGIND_OBJ_PATH, - NULL, - G_DBUS_SIGNAL_FLAGS_NONE, - handle_prepare_for_sleep_signal, - manager, - NULL); - /* Fake a resume as that triggers the inhibitor to be taken. */ - param_false = g_variant_new ("(b)", FALSE); - handle_prepare_for_sleep_signal (priv->connection, NULL, NULL, NULL, NULL, param_false, manager); - - /* And register the signals for initial enumeration and hotplug. */ - g_signal_connect_object (priv->context, - "device-added", - (GCallback) device_added_cb, - manager, - G_CONNECT_SWAPPED); - - g_signal_connect_object (priv->context, - "device-removed", - (GCallback) device_removed_cb, - manager, - G_CONNECT_SWAPPED); - - /* Prepare everything by enumerating all devices. - * This blocks the main loop until the existing devices are enumerated - */ - fp_context_enumerate (priv->context); - - G_OBJECT_CLASS (fprint_manager_parent_class)->constructed (object); -} - -static void -fprint_manager_init (FprintManager *manager) -{ -} - -FprintManager * -fprint_manager_new (GDBusConnection *connection, gboolean no_timeout) -{ - FprintManagerPrivate *priv; - GObject *object; - - object = g_object_new (FPRINT_TYPE_MANAGER, "connection", connection, NULL); - priv = fprint_manager_get_instance_private (FPRINT_MANAGER (object)); - priv->no_timeout = no_timeout; - - if (!priv->no_timeout) - priv->timeout_id = g_timeout_add_seconds (TIMEOUT, (GSourceFunc) fprint_manager_timeout_cb, object); - - return FPRINT_MANAGER (object); -} - -static gboolean -fprint_manager_get_devices (FprintManager *manager, - GPtrArray **devices, GError **error) -{ - FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager); - - g_autolist (FprintDBusObjectSkeleton) objects = NULL; - GList *l; - int num_open; - GPtrArray *devs; - - objects = g_dbus_object_manager_get_objects (priv->object_manager); - objects = g_list_reverse (objects); - - num_open = g_list_length (objects); - devs = g_ptr_array_sized_new (num_open); - - if (num_open > 0) - { - for (l = objects; l != NULL; l = l->next) - { - g_autoptr(FprintDevice) rdev = NULL; - FprintDBusObjectSkeleton *object = l->data; - const char *path; - - rdev = fprint_dbus_object_skeleton_get_device (object); - path = g_dbus_interface_skeleton_get_object_path ( - G_DBUS_INTERFACE_SKELETON (rdev)); - g_ptr_array_add (devs, (char *) path); - } - } - g_ptr_array_add (devs, NULL); - - *devices = devs; - return TRUE; -} - -static gboolean -fprint_manager_get_default_device (FprintManager *manager, - const char **device, GError **error) -{ - FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager); - - g_autolist (FprintDBusObjectSkeleton) objects = NULL; - int num_open; - - objects = g_dbus_object_manager_get_objects (priv->object_manager); - num_open = g_list_length (objects); - - if (num_open > 0) - { - g_autoptr(FprintDevice) rdev = NULL; - FprintDBusObjectSkeleton *object = g_list_last (objects)->data; - - rdev = fprint_dbus_object_skeleton_get_device (object); - *device = g_dbus_interface_skeleton_get_object_path ( - G_DBUS_INTERFACE_SKELETON (rdev)); - return TRUE; - } - else - { - g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_NO_SUCH_DEVICE, - "No devices available"); - *device = NULL; - return FALSE; - } -} - -GQuark -fprint_error_quark (void) -{ - static gsize quark = 0; - - if (g_once_init_enter (&quark)) - { - g_autoptr(GEnumClass) errors_enum = NULL; - GQuark domain; - unsigned i; - - domain = g_quark_from_static_string ("fprintd-error-quark"); - errors_enum = g_type_class_ref (FPRINT_TYPE_ERROR); - - for (i = 0; i < errors_enum->n_values; ++i) - { - GEnumValue *value = &errors_enum->values[i]; - - g_dbus_error_register_error (domain, value->value, - value->value_nick); - } - - g_once_init_leave (&quark, domain); - } - return (GQuark) quark; -} diff --git a/src/manager.xml b/src/manager.xml deleted file mode 100644 index 2d0ba28..0000000 --- a/src/manager.xml +++ /dev/null @@ -1,48 +0,0 @@ - -]> - - - - - - - - An array of object paths for devices. - - - - - - Enumerate all the fingerprint readers attached to the system. If there are - no devices available, an empty array is returned. - - - - - - - - - - The object path for the default device. - - - - - - Returns the default fingerprint reader device. - - - - - if the device does not exist - - - - - - - diff --git a/src/meson.build b/src/meson.build deleted file mode 100644 index 1df6216..0000000 --- a/src/meson.build +++ /dev/null @@ -1,93 +0,0 @@ -bash = find_program('bash') -dbus_interfaces = ['Manager', 'Device'] -dbus_interfaces_files = [] - -foreach interface_name: dbus_interfaces - interface = interface_name.to_lower() - interface_file = interface + '.xml' - dbus_interfaces_files += custom_target('dbus_interface_' + interface, - input: interface_file, - output: 'net.reactivated.Fprint.@0@.xml'.format(interface_name), - command: ['cp', '@INPUT@', '@OUTPUT@'], - install: true, - install_dir: dbus_interfaces_dir, - ) -endforeach - -# NOTE: We should pass "--glib-min-required 2.64" but cannot -fprintd_dbus_sources_base = gnome.gdbus_codegen('fprintd-dbus', - sources: dbus_interfaces_files, - autocleanup: 'all', - interface_prefix: 'net.reactivated.Fprint.', - namespace: 'FprintDBus', - object_manager: true, -) - -# FIXME: remove this and just use fprintd_dbus_sources when we're on glib 2.64 -fprintd_dbus_sources = [ - fprintd_dbus_sources_base[1] # header file -] - -fprintd_dbus_sources += custom_target('fprintd-dbus-interactive', - input: fprintd_dbus_sources_base[0], # c file, - output: 'fprintd-dbus-interactive.c', - command: [ - find_program('patch'), - '-p1', - '--merge', - '@INPUT@', - files('dbus-interactive-auth.patch'), - '-o', '@OUTPUT@', - ]) - -fprintd_enum_files = gnome.mkenums_simple('fprintd-enums', - sources: 'fprintd.h', -) - -fprintd_deps = declare_dependency( - include_directories: [ - include_directories('..'), - ], - sources: [ - fprintd_enum_files, - fprintd_dbus_sources, - ], - dependencies: [ - glib_dep, - gio_dep, - gio_unix_dep, - gmodule_dep, - libfprint_dep, - polkit_gobject_dep, - ], - compile_args: [ - '-DG_LOG_DOMAIN="@0@"'.format(meson.project_name()), - '-DLOCALEDIR="@0@"'.format(localedir), - '-DPLUGINDIR="@0@"'.format(fprintd_plugindir), - ], -) - -libfprintd_private = static_library('fprintd-private', - sources: [ - 'device.c', - 'fprintd.h', - 'manager.c', - ], - dependencies: fprintd_deps, - gnu_symbol_visibility: 'hidden', -) - -fprintd = executable('fprintd', - sources: [ - 'file_storage.c', - 'file_storage.h', - 'fprintd.h', - 'main.c', - 'storage.h', - ], - dependencies: fprintd_deps, - link_with: libfprintd_private, - gnu_symbol_visibility: 'hidden', - install: true, - install_dir: fprintd_installdir, -) diff --git a/src/storage.h b/src/storage.h deleted file mode 100644 index ef25a84..0000000 --- a/src/storage.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Simple file storage for fprintd - * Copyright (C) 2008 Vasily Khoruzhick - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#pragma once - -typedef int (*storage_print_data_save)(FpPrint *print); -typedef int (*storage_print_data_load)(FpDevice *dev, - FpFinger finger, - const char *username, - FpPrint **print); -typedef int (*storage_print_data_delete)(FpDevice *dev, - FpFinger finger, - const char *username); -typedef GSList *(*storage_discover_prints)(FpDevice *dev, - const char *username); -typedef GSList *(*storage_discover_users)(void); -typedef int (*storage_init)(void); -typedef int (*storage_deinit)(void); - -struct storage -{ - storage_init init; - storage_deinit deinit; - storage_print_data_save print_data_save; - storage_print_data_load print_data_load; - storage_print_data_delete print_data_delete; - storage_discover_prints discover_prints; - storage_discover_users discover_users; -}; - -typedef struct storage fp_storage; - -/* The currently setup store */ -extern fp_storage store; diff --git a/tests/LSAN-leaks-suppress.txt b/tests/LSAN-leaks-suppress.txt deleted file mode 100644 index a5fc940..0000000 --- a/tests/LSAN-leaks-suppress.txt +++ /dev/null @@ -1,4 +0,0 @@ -leak:initialize_device -leak:usbi_alloc_device -leak:libusb-1.0.so.* -leak:PyMem_RawMalloc diff --git a/tests/dbusmock/fprintd.py b/tests/dbusmock/fprintd.py deleted file mode 100644 index b4682a7..0000000 --- a/tests/dbusmock/fprintd.py +++ /dev/null @@ -1,433 +0,0 @@ -# -*- coding: utf-8 -*- - -'''fprintd mock template - -This creates the expected methods and properties of the -net.reactivated.Fprint.Manager object (/net/reactivated/Fprint/Manager) -but no devices. -''' - -# 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 sys -from gi.repository import GLib -import dbus -import asyncio - -from dbusmock import MOCK_IFACE, mockobject - -BUS_NAME = 'net.reactivated.Fprint' -MAIN_OBJ = '/net/reactivated/Fprint/Manager' -SYSTEM_BUS = True -IS_OBJECT_MANAGER = False - -MAIN_IFACE = 'net.reactivated.Fprint.Manager' -MANAGER_MOCK_IFACE = 'net.reactivated.Fprint.Manager.Mock' - -DEVICE_IFACE = 'net.reactivated.Fprint.Device' -DEVICE_MOCK_IFACE = 'net.reactivated.Fprint.Device.Mock' - -VALID_FINGER_NAMES = [ - 'left-thumb', - 'left-index-finger', - 'left-middle-finger', - 'left-ring-finger', - 'left-little-finger', - 'right-thumb', - 'right-index-finger', - 'right-middle-finger', - 'right-ring-finger', - 'right-little-finger' -] - -VALID_VERIFY_STATUS = [ - 'verify-no-match', - 'verify-match', - 'verify-retry-scan', - 'verify-swipe-too-short', - 'verify-finger-not-centered', - 'verify-remove-and-retry', - 'verify-disconnected', - 'verify-unknown-error' -] - -VALID_ENROLL_STATUS = [ - 'enroll-completed', - 'enroll-failed', - 'enroll-stage-passed', - 'enroll-retry-scan', - 'enroll-swipe-too-short', - 'enroll-finger-not-centered', - 'enroll-remove-and-retry', - 'enroll-data-full', - 'enroll-disconnected', - 'enroll-unknown-error' -] - -def load(mock, parameters): - fprintd = mockobject.objects[MAIN_OBJ] - mock.last_device_id = 0 - fprintd.fingers = {} - mock.loop = asyncio.new_event_loop() - -@dbus.service.method(MAIN_IFACE, - in_signature='', out_signature='ao') -def GetDevices(self): - return [(k) for k in mockobject.objects.keys() if "/Device/" in k] - -@dbus.service.method(MAIN_IFACE, - in_signature='', out_signature='o') -def GetDefaultDevice(self): - devices = self.GetDevices() - if len(devices) < 1: - raise dbus.exceptions.DBusException( - 'No devices available', - name='net.reactivated.Fprint.Error.NoSuchDevice') - return devices[0] - -@dbus.service.method(MANAGER_MOCK_IFACE, - in_signature='sisb', out_signature='s') -def AddDevice(self, device_name, num_enroll_stages, scan_type, - has_identification=False): - '''Convenience method to add a fingerprint reader device - - You have to specify a device name, the number of enrollment - stages it would use (> 0) and the scan type, as a string - (either 'press' or 'swipe') - ''' - - if scan_type not in ['swipe', 'press']: - raise dbus.exceptions.DBusException( - 'Invalid scan_type \'%s\'.' % scan_type, - name='org.freedesktop.DBus.Error.InvalidArgs') - - if num_enroll_stages <= 0: - raise dbus.exceptions.DBusException( - 'Invalid num_enroll_stages \'%s\'.' % num_enroll_stages, - name='org.freedesktop.DBus.Error.InvalidArgs') - - self.last_device_id += 1 - path = '/net/reactivated/Fprint/Device/%d' % self.last_device_id - device_properties = { - 'name': dbus.String(device_name, variant_level=1), - 'num-enroll-stages': dbus.Int32(num_enroll_stages, variant_level=1), - 'scan-type': scan_type - } - - self.AddObject(path, - DEVICE_IFACE, - # Properties - device_properties, - []) - - device = mockobject.objects[path] - device.fingers = {} - device.has_identification = has_identification - device.claimed_user = None - device.action = None - device.selected_finger = None - device.verify_script = [] - - return path - -@dbus.service.method(MANAGER_MOCK_IFACE, - in_signature='o') -def RemoveDevice(self, path): - # This isn't compatible with hotplugging devices, which fprintd doesn't - # support yet, but it's meant to remove devices added to the mock for - # testing purposes. - if not path: - raise dbus.exceptions.DBusException( - 'Invalid empty path.', - name='org.freedesktop.DBus.Error.InvalidArgs') - - self.RemoveObject(path) - -@dbus.service.method(DEVICE_IFACE, - in_signature='s', out_signature='as') -def ListEnrolledFingers(device, user): - if user in device.fingers: - return device.fingers[user] - raise dbus.exceptions.DBusException( - 'No enrolled prints in device %s for user %s' % (device.path, user), - name='net.reactivated.Fprint.Error.NoEnrolledPrints') - -@dbus.service.method(DEVICE_IFACE, - in_signature='s', out_signature='') -def DeleteEnrolledFingers(device, user): - device.fingers[user] = [] - -@dbus.service.method(DEVICE_IFACE, - in_signature='', out_signature='') -def DeleteEnrolledFingers2(device): - if not device.claimed_user: - raise dbus.exceptions.DBusException( - 'Device was not claimed before use', - name='net.reactivated.Fprint.Error.ClaimDevice') - if not device.fingers[device.claimed_user]: - raise dbus.exceptions.DBusException( - 'No enrolled prints in device {} for user {}'.format(device.path, - device.claimed_user), - name='net.reactivated.Fprint.Error.NoEnrolledPrints') - device.fingers[device.claimed_user] = [] - -@dbus.service.method(DEVICE_IFACE, - in_signature='s', out_signature='') -def Claim(device, user): - if device.claimed_user: - raise dbus.exceptions.DBusException( - 'Device already in use by %s' % device.claimed_user, - name='net.reactivated.Fprint.Error.AlreadyInUse') - - device.claimed_user = user - -@dbus.service.method(DEVICE_IFACE, - in_signature='', out_signature='') -def Release(device): - if not device.claimed_user: - raise dbus.exceptions.DBusException( - 'Device was not claimed before use', - name='net.reactivated.Fprint.Error.ClaimDevice') - device.claimed_user = None - device.action = None - device.selected_finger = None - -def can_verify_finger(device, finger_name): - # We should already have checked that there are enrolled fingers - if finger_name == 'any': - return True - if finger_name in device.fingers[device.claimed_user]: - return True - return False - -def glib_sleep(timeout): - waiting = True - - def done_waiting(): - nonlocal waiting - waiting = False - - GLib.timeout_add(timeout, done_waiting) - while (waiting): - GLib.main_context_default().iteration(True) - -def device_run_script(device, result, done): - if result == 'MOCK: quit': - sys.exit(0) - - # Emit signal - device.EmitSignal(DEVICE_IFACE, 'VerifyStatus', 'sb', [ - result, - done - ]) - -@dbus.service.method(DEVICE_IFACE, - in_signature='s', out_signature='') -def VerifyStart(device, finger_name): - if not device.claimed_user: - raise dbus.exceptions.DBusException( - 'Device was not claimed before use', - name='net.reactivated.Fprint.Error.ClaimDevice') - if device.claimed_user not in device.fingers: - raise dbus.exceptions.DBusException( - 'No enrolled prints for user \'%s\'' % device.claimed_user, - name='net.reactivated.Fprint.Error.NoEnrolledPrints') - if not finger_name: - raise dbus.exceptions.DBusException( - 'Invalid empty finger_name.', - name='org.freedesktop.DBus.Error.InvalidArgs') - if not can_verify_finger(device, finger_name): - raise dbus.exceptions.DBusException( - 'Finger \'%s\' not enrolled.' % finger_name, - name='org.freedesktop.DBus.Error.Internal') - if device.action: - raise dbus.exceptions.DBusException( - 'Action \'%s\' already in progress' % device.action, - name='net.reactivated.Fprint.Error.AlreadyInUse') - device.action = 'verify' - - if finger_name == 'any' and not device.has_identification: - finger_name = device.fingers[device.claimed_user][0] - device.selected_finger = finger_name - # Needs to happen after method return - GLib.idle_add(device.EmitSignal, - DEVICE_IFACE, 'VerifyFingerSelected', 's', [ - device.selected_finger - ]) - - error = None - base_delay = 0 - while device.verify_script is not None and len(device.verify_script) > 0: - result, done, timeout = device.verify_script.pop(0) - - # We stop when "timeout >= 0 and done" - if result == 'MOCK: no-prints': - # Special case to change return value of DBus call, ignores timeout - error = dbus.exceptions.DBusException( - 'No enrolled prints for user \'%s\'' % device.claimed_user, - name='net.reactivated.Fprint.Error.NoEnrolledPrints') - - elif timeout < 0: - # Negative timeouts mean emitting before the DBus call returns - device_run_script(device, result, done) - glib_sleep(-timeout) - - else: - # Positive or zero means emitting afterwards the given timeout - base_delay += timeout - GLib.timeout_add(base_delay, - device_run_script, - device, - result, - done) - - # Stop processing commands when the done flag is set - if done: - break - - if error: - raise error - -@dbus.service.method(DEVICE_MOCK_IFACE, - in_signature='sb', out_signature='') -def EmitVerifyStatus(device, result, done): - if (not device.action) or (device.action != 'verify'): - raise dbus.exceptions.DBusException( - 'Cannot send verify statuses when not verifying', - name='org.freedesktop.DBus.Error.InvalidArgs') - if result not in VALID_VERIFY_STATUS: - raise dbus.exceptions.DBusException( - 'Unknown verify status \'%s\'' % result, - name='org.freedesktop.DBus.Error.InvalidArgs') - device.EmitSignal(DEVICE_IFACE, 'VerifyStatus', 'sb', [ - result, - done - ]) - -@dbus.service.method(DEVICE_IFACE, - in_signature='', out_signature='') -def VerifyStop(device): - if device.action != 'verify': - raise dbus.exceptions.DBusException( - 'No verification to stop', - name='net.reactivated.Fprint.Error.NoActionInProgress') - device.action = None - device.selected_finger = None - -@dbus.service.method(DEVICE_IFACE, - in_signature='s', out_signature='') -def EnrollStart(device, finger_name): - if finger_name not in VALID_FINGER_NAMES: - raise dbus.exceptions.DBusException( - 'Invalid finger name \'%s\'' % finger_name, - name='net.reactivated.Fprint.Error.InvalidFingername') - if not device.claimed_user: - raise dbus.exceptions.DBusException( - 'Device was not claimed before use', - name='net.reactivated.Fprint.Error.ClaimDevice') - if device.action: - raise dbus.exceptions.DBusException( - 'Action \'%s\' already in progress' % device.action, - name='net.reactivated.Fprint.Error.AlreadyInUse') - device.action = 'enroll' - -@dbus.service.method(DEVICE_MOCK_IFACE, - in_signature='sb', out_signature='') -def EmitEnrollStatus(device, result, done): - if (not device.action) or (device.action != 'enroll'): - raise dbus.exceptions.DBusException( - 'Cannot send enroll statuses when not enrolling', - name='org.freedesktop.DBus.Error.InvalidArgs') - if result not in VALID_ENROLL_STATUS: - raise dbus.exceptions.DBusException( - 'Unknown enroll status \'%s\'' % result, - name='org.freedesktop.DBus.Error.InvalidArgs') - device.EmitSignal(DEVICE_IFACE, 'EnrollStatus', 'sb', [ - result, - done - ]) - # FIXME save enrolled finger? - -@dbus.service.method(DEVICE_IFACE, - in_signature='', out_signature='') -def EnrollStop(device): - if device.action != 'enroll': - raise dbus.exceptions.DBusException( - 'No enrollment to stop', - name='net.reactivated.Fprint.Error.NoActionInProgress') - device.action = None - -@dbus.service.method(DEVICE_MOCK_IFACE, - in_signature='sas', out_signature='') -def SetEnrolledFingers(device, user, fingers): - '''Convenience method to set the list of enrolled fingers. - - The device_path is the return value from AddDevice(), and the - array of fingers must only contain valid finger names. - - Returns nothing. - ''' - - for k in fingers: - if k not in VALID_FINGER_NAMES: - raise dbus.exceptions.DBusException( - 'Invalid finger name \'%s\'' % k, - name='org.freedesktop.DBus.Error.InvalidArgs') - - device.fingers[user] = fingers - -@dbus.service.method(DEVICE_MOCK_IFACE, - in_signature='', out_signature='s') -def GetSelectedFinger(device): - '''Convenience method to get the finger under verification - - Returns the finger name that the user has selected for verifying - ''' - if not device.selected_finger: - raise dbus.exceptions.DBusException( - 'Device is not verifying', - name='net.reactivated.Fprint.Error.NoActionInProgress') - - return device.selected_finger - -@dbus.service.method(DEVICE_MOCK_IFACE, - in_signature='', out_signature='b') -def HasIdentification(device): - '''Convenience method to get if a device supports identification - - Returns whether identification is supported. - ''' - - return device.has_identification - -@dbus.service.method(DEVICE_MOCK_IFACE, - in_signature='a(sbi)', out_signature='') -def SetVerifyScript(device, script): - '''Convenience method to set the verification script. - - After VerifyStart is called, signal results will be sent in order after - a certain timeout declared in seconds. The array contains each - 'result' followed by the 'done' argument for VerifyStatus, and the - amount of time to wait before each signal is sent. - - Returns nothing. - ''' - - device.verify_script = script - -@dbus.service.method(DEVICE_MOCK_IFACE, - in_signature='s', out_signature='') -def SetClaimed(device, user): - if user == '': - device.claimed_user = None - else: - device.claimed_user = user diff --git a/tests/dbusmock/polkitd.py b/tests/dbusmock/polkitd.py deleted file mode 100644 index 3f4d078..0000000 --- a/tests/dbusmock/polkitd.py +++ /dev/null @@ -1,101 +0,0 @@ -# -*- coding: utf-8 -*- - -'''polkit mock template - -This creates the basic methods and properties of the -org.freedesktop.PolicyKit1.Authority object, so that we can use it async -''' - -# 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__ = 'Marco Trevisan' -__email__ = 'marco.trevisan@canonical.com' -__copyright__ = '(c) 2020 Canonical Ltd.' -__license__ = 'LGPL 3+' - -import dbus -import time - -from dbusmock import MOCK_IFACE - -BUS_NAME = 'org.freedesktop.PolicyKit1' -MAIN_OBJ = '/org/freedesktop/PolicyKit1/Authority' -MAIN_IFACE = 'org.freedesktop.PolicyKit1.Authority' -SYSTEM_BUS = True -IS_OBJECT_MANAGER = False - -def load(mock, parameters): - mock.allow_unknown = False - mock.allowed = [] - mock.delay = 0 - mock.simulate_hang = False - mock.hanging_actions = [] - mock.hanging_calls = [] - - mock.AddProperties(MAIN_IFACE, - dbus.Dictionary({ - 'BackendName': 'local', - 'BackendVersion': '0.8.15', - 'BackendFeatures': dbus.UInt32(1, variant_level=1), - }, signature='sv')) - - -@dbus.service.method(MAIN_IFACE, - in_signature='(sa{sv})sa{ss}us', out_signature='(bba{ss})', - async_callbacks=('ok_cb', 'err_cb')) -def CheckAuthorization(self, subject, action_id, details, flags, cancellation_id, - ok_cb, err_cb): - time.sleep(self.delay) - allowed = action_id in self.allowed or self.allow_unknown - ret = (allowed, False, {'test': 'test'}) - - if self.simulate_hang or action_id in self.hanging_actions: - self.hanging_calls.append((ok_cb, ret)) - else: - ok_cb(ret) - -@dbus.service.method(MOCK_IFACE, in_signature='b', out_signature='') -def AllowUnknown(self, default): - '''Control whether unknown actions are allowed - - This controls the return value of CheckAuthorization for actions which were - not explicitly allowed by SetAllowed(). - ''' - self.allow_unknown = default - -@dbus.service.method(MOCK_IFACE, in_signature='d', out_signature='') -def SetDelay(self, delay): - '''Makes the CheckAuthorization() method to delay''' - self.delay = delay - -@dbus.service.method(MOCK_IFACE, in_signature='b', out_signature='') -def SimulateHang(self, hang): - '''Makes the CheckAuthorization() method to hang''' - self.simulate_hang = hang - -@dbus.service.method(MOCK_IFACE, in_signature='as', out_signature='') -def SimulateHangActions(self, actions): - '''Makes the CheckAuthorization() method to hang on such actions''' - self.hanging_actions = actions - -@dbus.service.method(MOCK_IFACE, in_signature='', out_signature='') -def ReleaseHangingCalls(self): - '''Calls all the hanging callbacks''' - for (cb, ret) in self.hanging_calls: - cb(ret) - self.hanging_calls = [] - -@dbus.service.method(MOCK_IFACE, in_signature='', out_signature='b') -def HaveHangingCalls(self): - '''Check if we've hangling calls''' - return len(self.hanging_calls) - -@dbus.service.method(MOCK_IFACE, in_signature='as', out_signature='') -def SetAllowed(self, actions): - '''Set allowed actions''' - - self.allowed = actions diff --git a/tests/fprintd.py b/tests/fprintd.py deleted file mode 100644 index 64ea42e..0000000 --- a/tests/fprintd.py +++ /dev/null @@ -1,3440 +0,0 @@ -#! /usr/bin/env python3 -# Copyright © 2017, 2019 Red Hat, Inc -# Copyright © 2020 Canonical Ltd -# -# 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 2.1 of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. If not, see . -# Authors: -# Christian J. Kellner -# Benjamin Berg -# Marco Trevisan - -import unittest -import time -import subprocess -import os -import os.path -import sys -import tempfile -import glob -import pwd -import re -import shutil -import socket -import struct -import dbusmock -import gi -gi.require_version('FPrint', '2.0') -from gi.repository import GLib, Gio, FPrint -from output_checker import OutputChecker -import cairo -import signal - -try: - from subprocess import DEVNULL -except ImportError: - DEVNULL = open(os.devnull, 'wb') - -FPRINT_NAMESPACE = 'net.reactivated.Fprint' -FPRINT_PATH = '/' + FPRINT_NAMESPACE.replace('.', '/') -SERVICE_FILE = '/usr/share/dbus-1/system-services/{}.service'.format(FPRINT_NAMESPACE) - -class FprintDevicePermission: - verify = FPRINT_NAMESPACE.lower() + '.device.verify' - enroll = FPRINT_NAMESPACE.lower() + '.device.enroll' - set_username = FPRINT_NAMESPACE.lower() + '.device.setusername' - - -FINGERS_MAP = { - "left-thumb": FPrint.Finger.LEFT_THUMB, - "left-index-finger": FPrint.Finger.LEFT_INDEX, - "left-middle-finger": FPrint.Finger.LEFT_MIDDLE, - "left-ring-finger": FPrint.Finger.LEFT_RING, - "left-little-finger": FPrint.Finger.LEFT_LITTLE, - "right-thumb": FPrint.Finger.RIGHT_THUMB, - "right-index-finger": FPrint.Finger.RIGHT_INDEX, - "right-middle-finger": FPrint.Finger.RIGHT_MIDDLE, - "right-ring-finger": FPrint.Finger.RIGHT_RING, - "right-little-finger": FPrint.Finger.RIGHT_LITTLE, - "any": FPrint.Finger.UNKNOWN, -} - -def get_timeout(topic='default'): - vals = { - 'valgrind': { - 'test': 300, - 'device_sleep': 600, - 'default': 20, - 'daemon_start': 60, - 'daemon_stop': 10, - }, - 'asan': { - 'test': 120, - 'default': 6, - 'device_sleep': 400, - 'daemon_start': 10, - 'daemon_stop': 8, - }, - 'default': { - 'test': 60, - 'device_sleep': 100, - 'default': 3, - 'daemon_start': 5, - 'daemon_stop': 2, - } - } - - if os.getenv('VALGRIND') is not None: - lut = vals['valgrind'] - elif os.getenv('ADDRESS_SANITIZER') is not None: - lut = vals['asan'] - else: - lut = vals['default'] - - if topic not in lut: - raise ValueError('invalid topic') - return lut[topic] - - -# Copied from libfprint tests -class Connection: - - def __init__(self, addr): - self.addr = addr - - def __enter__(self): - self.con = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - self.con.connect(self.addr) - return self.con - - def __exit__(self, exc_type, exc_val, exc_tb): - self.con.close() - del self.con - -# Speed up tests by only loading a 128x128px area from the center -MAX_IMG_SIZE = 128 -def load_image(img): - png = cairo.ImageSurface.create_from_png(img) - - # Cairo wants 4 byte aligned rows, so just add a few pixel if necessary - w = png.get_width() - h = png.get_height() - w = (w + 3) // 4 * 4 - h = (h + 3) // 4 * 4 - - w_out = min(MAX_IMG_SIZE, w) - h_out = min(MAX_IMG_SIZE, h) - x = (w - w_out) // 2 - y = (h - h_out) // 2 - - img = cairo.ImageSurface(cairo.Format.A8, w_out, h_out) - cr = cairo.Context(img) - - cr.set_source_rgba(1, 1, 1, 1) - cr.paint() - - cr.set_source_rgba(0, 0, 0, 0) - cr.set_operator(cairo.OPERATOR_SOURCE) - - cr.set_source_surface(png, -x, -y) - cr.paint() - - return img - -if hasattr(os.environ, 'TOPSRCDIR'): - root = os.environ['TOPSRCDIR'] -else: - root = os.path.join(os.path.dirname(__file__), '..') - -imgdir = os.path.join(root, 'tests', 'prints') - -ctx = GLib.main_context_default() - -class FPrintdTest(dbusmock.DBusTestCase): - - @staticmethod - def path_from_service_file(sf): - with open(SERVICE_FILE) as f: - for line in f: - if not line.startswith('Exec='): - continue - return line.split('=', 1)[1].strip() - return None - - @classmethod - def setUpClass(cls): - # Try to generate backtrace if meson kills as with SIGTERM - def r(*args): - raise KeyboardInterrupt() - signal.signal(signal.SIGTERM, r) - - super().setUpClass() - fprintd = None - cls._polkitd = None - - cls._has_hotplug = FPrint.Device.find_property("removed") is not None - - if 'FPRINT_BUILD_DIR' in os.environ: - print('Testing local build') - build_dir = os.environ['FPRINT_BUILD_DIR'] - fprintd = os.path.join(build_dir, 'fprintd') - elif 'UNDER_JHBUILD' in os.environ: - print('Testing JHBuild version') - jhbuild_prefix = os.environ['JHBUILD_PREFIX'] - fprintd = os.path.join(jhbuild_prefix, 'libexec', 'fprintd') - else: - print('Testing installed system binaries') - fprintd = cls.path_from_service_file(SERVICE_FILE) - - assert fprintd is not None, 'failed to find daemon' - cls.paths = {'daemon': fprintd } - - - cls.tmpdir = tempfile.mkdtemp(prefix='libfprint-') - cls.addClassCleanup(shutil.rmtree, cls.tmpdir) - - cls.sockaddr = os.path.join(cls.tmpdir, 'virtual-image.socket') - os.environ[cls.socket_env] = cls.sockaddr - - cls.prints = {} - for f in glob.glob(os.path.join(imgdir, '*.png')): - n = os.path.basename(f)[:-4] - cls.prints[n] = load_image(f) - - cls.start_system_bus() - cls.dbus = Gio.DBusConnection.new_for_address_sync(os.environ['DBUS_SYSTEM_BUS_ADDRESS'], - Gio.DBusConnectionFlags.MESSAGE_BUS_CONNECTION | - Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT, None, None) - assert cls.dbus.is_closed() == False - cls.addClassCleanup(cls.dbus.close) - - @classmethod - def tearDownClass(cls): - dbusmock.DBusTestCase.tearDownClass() - - del cls.dbus - - def daemon_start(self, driver='Virtual image device for debugging'): - timeout = get_timeout('daemon_start') # seconds - env = os.environ.copy() - env['G_DEBUG'] = 'fatal-criticals' - env['STATE_DIRECTORY'] = (self.state_dir + ':' + '/hopefully/a/state_dir_path/that/shouldnt/be/writable') - env['RUNTIME_DIRECTORY'] = self.run_dir - # The tests parses the debug output for suspend inhibitor debugging - env['G_MESSAGES_DEBUG'] = 'all' - - argv = [self.paths['daemon'], '-t'] - valgrind = os.getenv('VALGRIND') - if valgrind is not None: - argv.insert(0, 'valgrind') - argv.insert(1, '--leak-check=full') - if os.path.exists(valgrind): - argv.insert(2, '--suppressions=%s' % valgrind) - self.valgrind = True - self.kill_daemon = False - self.daemon_log = OutputChecker() - self.addCleanup(self.daemon_log.force_close) - self.daemon = subprocess.Popen(argv, - env=env, - stdout=self.daemon_log.fd, - stderr=subprocess.STDOUT) - self.daemon_log.writer_attached() - - #subprocess.Popen(['/usr/bin/dbus-monitor', '--system']) - - self.addCleanup(self.daemon_stop) - - timeout_count = timeout * 10 - timeout_sleep = 0.1 - while timeout_count > 0: - time.sleep(timeout_sleep) - timeout_count -= 1 - try: - self.manager = Gio.DBusProxy.new_sync(self.dbus, - Gio.DBusProxyFlags.DO_NOT_AUTO_START, - None, - FPRINT_NAMESPACE, - FPRINT_PATH + '/Manager', - FPRINT_NAMESPACE + '.Manager', - None) - - devices = self.manager.GetDevices() - # Find the virtual device, just in case it is a local run - # and there is another usable sensor available locally - for path in devices: - dev = Gio.DBusProxy.new_sync(self.dbus, - Gio.DBusProxyFlags.DO_NOT_AUTO_START, - None, - FPRINT_NAMESPACE, - path, - FPRINT_NAMESPACE + '.Device', - None) - - if driver in str(dev.get_cached_property('name')): - self.device = dev - self._device_cancellable = Gio.Cancellable() - self.addCleanup(self._device_cancellable.cancel) - break - else: - print('Did not find virtual device! Probably libfprint was build without the corresponding driver!') - - break - except GLib.GError: - pass - else: - timeout_time = timeout * 10 * timeout_sleep - self.fail('daemon did not start in %d seconds' % timeout_time) - - def daemon_stop(self): - - if self.daemon: - try: - self.daemon.terminate() - except OSError: - pass - - try: - self.daemon.wait(timeout=get_timeout('daemon_stop')) - except subprocess.TimeoutExpired as e: - if self.kill_daemon: - self.daemon.kill() - else: - raise(e) - - self.daemon_log.assert_closed() - - if not self.kill_daemon: - self.assertLess(self.daemon.returncode, 128) - self.assertGreaterEqual(self.daemon.returncode, 0) - - self.daemon = None - - def polkitd_start(self): - if self._polkitd: - return - - if 'POLKITD_MOCK_PATH' in os.environ: - polkitd_template = os.path.join(os.getenv('POLKITD_MOCK_PATH'), 'polkitd.py') - else: - polkitd_template = os.path.join(os.path.dirname(__file__), 'dbusmock/polkitd.py') - print ('Using template from %s' % polkitd_template) - - self._polkitd, self._polkitd_obj = self.spawn_server_template( - polkitd_template, {}, stdout=subprocess.PIPE) - self.addCleanup(self.stop_server, '_polkitd', '_polkitd_obj') - - return self._polkitd - - def stop_server(self, proc_attr, obj_attr): - proc = getattr(self, proc_attr, None) - if proc is None: - return - - proc.terminate() - try: - proc.wait(timeout=1) - except subprocess.TimeoutExpired as e: - proc.kill() - - delattr(self, proc_attr) - delattr(self, obj_attr) - - def polkitd_allow_all(self): - self._polkitd_obj.SetAllowed([FprintDevicePermission.set_username, - FprintDevicePermission.enroll, - FprintDevicePermission.verify]) - - def get_current_user(self): - return pwd.getpwuid(os.getuid()).pw_name - - def setUp(self): - self.test_dir = tempfile.mkdtemp() - self.addCleanup(shutil.rmtree, self.test_dir) - self.state_dir = os.path.join(self.test_dir, 'state') - self.run_dir = os.path.join(self.test_dir, 'run') - self.device_id = 0 - self._async_call_res = {} - os.environ['FP_DRIVERS_WHITELIST'] = self.device_driver - - # Always start fake polkitd because of - # https://gitlab.freedesktop.org/polkit/polkit/-/merge_requests/95 - self.polkitd_start() - - def assertFprintError(self, fprint_error): - if isinstance(fprint_error, list) or isinstance(fprint_error, tuple): - fprint_error = [ re.escape(e) for e in fprint_error ] - fprint_error = '({})'.format('|'.join(fprint_error)) - else: - fprint_error = re.escape(fprint_error) - - return self.assertRaisesRegex(GLib.Error, - re.escape('GDBus.Error:{}.Error.'.format(FPRINT_NAMESPACE)) + - '{}:'.format(fprint_error)) - - def skipTestIfCanWrite(self, path): - try: - os.open(os.path.join(path, "testfile"), os.O_CREAT | os.O_WRONLY) - self.skipTest('Permissions aren\'t respected (CI environment?)') - except PermissionError: - pass - - def get_print_file_path(self, user, finger): - return os.path.join(self.state_dir, user, self.device_driver, - str(self.device_id), str(int(finger))) - - def get_print_name_file_path(self, user, finger_name): - return self.get_print_file_path(user, FINGERS_MAP[finger_name]) - - def get_finger_name(self, finger): - return {v: k for k, v in FINGERS_MAP.items()}[finger] - - def set_print_not_writable(self, user, finger): - # Replace the print with a directory, so that deletion via unlink will fail - # But it is still listed, not using chmod as it won't work in CI environment - print_file = self.get_print_file_path(user, finger) - if os.path.exists(print_file): - os.rename(print_file, print_file + '_renamed') - self.addCleanup(os.rename, print_file + '_renamed', print_file) - os.makedirs(print_file) - self.addCleanup(os.rmdir, print_file) - - def assertFingerInStorage(self, user, finger): - self.assertTrue(os.path.exists(self.get_print_file_path(user, finger))) - - def assertFingerNotInStorage(self, user, finger): - self.assertFalse(os.path.exists(self.get_print_file_path(user, finger))) - - @property - def finger_needed(self): - return self.device.get_cached_property('finger-needed').unpack() - - @property - def finger_present(self): - return self.device.get_cached_property('finger-present').unpack() - - @property - def num_enroll_stages(self): - return self.device.get_cached_property('num-enroll-stages').unpack() - - # From libfprint tests - def send_retry(self, retry_error=FPrint.DeviceRetry.TOO_SHORT, con=None): - if con: - con.sendall(struct.pack('ii', -1, retry_error)) - return - - with Connection(self.sockaddr) as con: - self.send_retry(retry_error, con) - - # From libfprint tests - def send_error(self, error=FPrint.DeviceError.GENERAL, con=None): - if con: - con.sendall(struct.pack('ii', -2, error)) - return - - with Connection(self.sockaddr) as con: - self.send_error(error, con) - - # From libfprint tests - def send_remove(self, con=None): - if con: - con.sendall(struct.pack('ii', -5, 0)) - return - - with Connection(self.sockaddr) as con: - self.send_remove(con=con) - - # From libfprint tests - def send_image(self, image, con=None): - if con: - img = self.prints[image] - mem = img.get_data() - mem = mem.tobytes() - self.assertEqual(len(mem), img.get_width() * img.get_height()) - - encoded_img = struct.pack('ii', img.get_width(), img.get_height()) - encoded_img += mem - - con.sendall(encoded_img) - return - - with Connection(self.sockaddr) as con: - self.send_image(image, con) - - def send_finger_automatic(self, automatic, con=None, iterate=True): - # Set whether finger on/off is reported around images - if con: - con.sendall(struct.pack('ii', -3, 1 if automatic else 0)) - return - - with Connection(self.sockaddr) as con: - self.send_finger_automatic(automatic, con=con, iterate=iterate) - - while iterate and ctx.pending(): - ctx.iteration(False) - - def send_finger_report(self, has_finger, con=None, iterate=True): - # Send finger on/off - if con: - con.sendall(struct.pack('ii', -4, 1 if has_finger else 0)) - return - - with Connection(self.sockaddr) as con: - self.send_finger_report(has_finger, con=con) - - while iterate and self.finger_present != has_finger: - ctx.iteration(False) - - def send_sleep(self, con=None): - self.skipTest('Not implemented for {}'.format(self.device_driver)) - - def set_keep_alive(self, value): - self.skipTest('Not implemented for {}'.format(self.device_driver)) - - def _maybe_reduce_enroll_stages(self): - pass - - def call_proxy_method_async(self, proxy, method, *args): - def call_handler(proxy, res): - nonlocal method - - if proxy not in self._async_call_res.keys(): - self._async_call_res[proxy] = {} - if method not in self._async_call_res[proxy].keys(): - self._async_call_res[proxy][method] = [] - - try: - ret = proxy.call_finish(res) - except Exception as e: - ret = e - self._async_call_res[proxy][method].append(ret) - - self.device.call(method, GLib.Variant(*args), - Gio.DBusCallFlags.NONE, -1, self._device_cancellable, - call_handler) - - def call_device_method_async(self, method, *args): - return self.call_proxy_method_async(self.device, method, *args) - - def wait_for_async_reply(self, proxy, method=None, expected_replies=1): - if proxy in self._async_call_res: - proxy_replies = self._async_call_res[proxy] - if method and method in proxy_replies: - proxy_replies[method] = [] - else: - proxy_replies = {} - - def get_replies(): - nonlocal proxy, method - return (self.get_all_async_replies(proxy=proxy) if not method - else self.get_async_replies(proxy=proxy, method=method)) - - while len(get_replies()) != expected_replies: - ctx.iteration(True) - - for res in get_replies(): - if isinstance(res, Exception): - raise res - - def wait_for_device_reply(self, method=None, expected_replies=1): - return self.wait_for_async_reply(self.device, method=method, - expected_replies=expected_replies) - - def wait_for_device_reply_relaxed(self, method=None, expected_replies=1, accepted_exceptions=[]): - try: - self.wait_for_device_reply(method=method, expected_replies=expected_replies) - except GLib.Error as e: - for ae in accepted_exceptions: - if 'GDBus.Error:{}.Error.{}'.format(FPRINT_NAMESPACE, ae) in str(e): - return - raise(e) - - def get_async_replies(self, method=None, proxy=None): - method_calls = self._async_call_res.get(proxy if proxy else self.device, {}) - return method_calls.get(method, []) if method else method_calls - - def get_all_async_replies(self, proxy=None): - method_calls = self.get_async_replies(proxy=proxy) - all_replies = [] - for method, replies in method_calls.items(): - all_replies.extend(replies) - return all_replies - - def gdbus_device_method_call_process(self, method, args=[]): - return subprocess.Popen([ - 'gdbus', - 'call', - '--system', - '--dest', - self.device.get_name(), - '--object-path', - self.device.get_object_path(), - '--method', - '{}.{}'.format(self.device.get_interface_name(), method), - ] + args, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) - - def call_device_method_from_other_client(self, method, args=[]): - try: - proc = self.gdbus_device_method_call_process(method, args) - proc.wait(timeout=5) - if proc.returncode != 0: - raise GLib.GError(proc.stdout.read()) - return proc.stdout.read() - except subprocess.TimeoutExpired as e: - raise GLib.GError(e.output) - - -class FPrintdVirtualImageDeviceBaseTests(FPrintdTest): - socket_env = 'FP_VIRTUAL_IMAGE' - device_driver = 'virtual_image' - driver_name = 'Virtual image device for debugging' - has_identification = True - -class FPrintdVirtualDeviceBaseTest(FPrintdVirtualImageDeviceBaseTests): - - def setUp(self): - super().setUp() - - self.manager = None - self.device = None - - fifo_path = os.path.join(self.tmpdir, 'logind_inhibit_fifo') - os.mkfifo(fifo_path) - self.addCleanup(os.unlink, fifo_path) - self.logind_inhibit_fifo = os.open(fifo_path, os.O_RDONLY | os.O_NONBLOCK | os.O_CLOEXEC) - self.addCleanup(os.close, self.logind_inhibit_fifo) - # EOF without a writer, BlockingIOError with a writer - self.assertFalse(self.holds_inhibitor()) - - self.logind, self.logind_obj = self.spawn_server_template('logind', { }) - self.logind_obj.AddMethod('org.freedesktop.login1.Manager', 'Inhibit', 'ssss', 'h', - 'ret = os.open("%s", os.O_WRONLY)\n' % fifo_path + - 'from gi.repository import GLib\n' + - 'GLib.idle_add(lambda fd: os.close(fd), ret)') - self.addCleanup(self.stop_server, 'logind', 'logind_obj') - self.daemon_start(self.driver_name) - - self.wait_got_delay_inhibitor(timeout=5) - - if self.device is None: - self.skipTest("Need {} device to run the test".format(self.device_driver)) - - self._polkitd_obj.SetAllowed([FprintDevicePermission.set_username, - FprintDevicePermission.enroll, - FprintDevicePermission.verify]) - - def signal_cb(proxy, sender, signal, params): - print(signal, params) - if signal == 'EnrollStatus': - self._abort = params[1] - self._last_result = params[0] - - if not self._abort and self._last_result.startswith('enroll-'): - # Exit wait loop, onto next enroll state (if any) - self._abort = True - elif self._abort: - pass - else: - self._abort = True - self._last_result = 'Unexpected signal values' - print('Unexpected signal values') - elif signal == 'VerifyFingerSelected': - self._selected_finger = params[0] - elif signal == 'VerifyStatus': - self._abort = True - self._last_result = params[0] - self._verify_stopped = params[1] - else: - self._abort = True - self._last_result = 'Unexpected signal' - - def property_cb(proxy, changed, invalidated): - print('Changed properties', changed, 'invalidated', invalidated) - self._changed_properties.append(changed.unpack()) - - signal_id = self.device.connect('g-signal', signal_cb) - self.addCleanup(self.device.disconnect, signal_id) - - signal_id = self.device.connect('g-properties-changed', property_cb) - self.addCleanup(self.device.disconnect, signal_id) - self._changed_properties = [] - - def tearDown(self): - self.device = None - self.manager = None - - super().tearDown() - - def try_release(self): - if not self.device: - return - try: - self.device.Release() - except GLib.GError as e: - if not 'net.reactivated.Fprint.Error.ClaimDevice' in e.message: - raise(e) - - def wait_for_result(self, expected=None, max_wait=-1): - self._last_result = None - self._verify_stopped = False - self._selected_finger = None - self._abort = False - - if max_wait > 0: - def abort_timeout(): - self._abort = True - GLib.timeout_add(max_wait, abort_timeout) - - while not self._abort: - ctx.iteration(True) - - self.assertTrue(self._abort) - self._abort = False - - if expected is not None: - self.assertEqual(self._last_result, expected) - - def holds_inhibitor(self): - try: - if os.read(self.logind_inhibit_fifo, 1) == b'': - return False - except BlockingIOError: - return True - - raise AssertionError("logind inhibitor fifo in unexpected state") - - def wait_got_delay_inhibitor(self, timeout=0): - self.daemon_log.check_line('Got delay inhibitor for sleep', timeout=timeout) - self.assertTrue(self.holds_inhibitor()) - - def wait_released_delay_inhibitor(self, timeout=0): - self.daemon_log.check_line('Released delay inhibitor for sleep', timeout=timeout) - self.assertFalse(self.holds_inhibitor()) - - def enroll_image(self, img, device=None, finger='right-index-finger', - expected_result='enroll-completed', claim_user=None, - start=True, stop=True): - if device is None: - device = self.device - if claim_user: - device.Claim('(s)', claim_user) - - if device is self.device: - self._maybe_reduce_enroll_stages() - - if start: - device.EnrollStart('(s)', finger) - - while not self.finger_needed: - ctx.iteration(False) - self.assertTrue(self.finger_needed) - - if expected_result == 'enroll-duplicate': - stages = 1 - else: - stages = self.num_enroll_stages - - for stage in range(stages): - self.send_image(img) - if stage < stages - 1: - self.wait_for_result('enroll-stage-passed') - else: - self.wait_for_result(expected_result) - self.assertFalse(self.finger_needed) - - if stop: - device.EnrollStop() - - self.assertEqual(self._last_result, expected_result) - self.assertFalse(self.finger_needed) - - if claim_user: - device.Release() - - def enroll_multiple_images(self, images_override={}, return_index=-1): - enroll_map = { - 'left-thumb': 'whorl', - 'right-index-finger': 'arch', - 'left-little-finger': 'loop-right', - } - enroll_map.update(images_override) - - for finger, print in enroll_map.items(): - self.enroll_image(print, finger=finger) - - enrolled = self.device.ListEnrolledFingers('(s)', 'testuser') - self.assertCountEqual(enroll_map.keys(), enrolled) - - if return_index >= 0: - return enroll_map[enrolled[return_index]] - - return (enrolled, enroll_map) - - def enroll_users_images(self, enroll_map={}, images_override={}, allow_duplicates=False): - if not enroll_map: - enroll_map = { - 'test-user1': { 'left-thumb': 'whorl' }, - 'test-user2': { 'right-index-finger': 'arch' }, - 'test-user3': { 'left-little-finger': 'loop-right', - 'left-thumb': 'tented_arch' }, - } - enroll_map.update(images_override) - - enrolled_prints = [] - enrolled_prints_info = {} - duplicates_prints_info = {} - - self.try_release() - - for user, print_map in enroll_map.items(): - self.device.Claim('(s)', user) - for finger, p in print_map.items(): - if allow_duplicates and p in enrolled_prints: - prints_infos = duplicates_prints_info.get(p, []) - prints_infos.append((user, finger)) - duplicates_prints_info[p] = prints_infos - if self.has_identification: - self.enroll_image(p, finger=finger, - expected_result='enroll-duplicate') - continue - self.enroll_image(p, finger=finger) - enrolled_prints.append(p) - enrolled_prints_info[p] = (user, finger) - self.device.Release() - - if allow_duplicates and duplicates_prints_info: - # We can't just enroll duplicates prints, as fprint will check for - # duplicates prints, so we've to handle this manually, copying the - # actual prints - for print_image, print_infos in duplicates_prints_info.items(): - for print_info in print_infos: - orig_username, orig_finger = enrolled_prints_info[print_image] - dup_username, dup_finger = print_info - dup_fp_finger = FINGERS_MAP[dup_finger] - - orig_path = self.get_print_name_file_path(orig_username, orig_finger) - self.assertTrue(os.path.exists(orig_path)) - - with open(orig_path, mode='rb') as print_file: - dup_print = FPrint.Print.deserialize(print_file.read()) - dup_print.set_username(dup_username) - dup_print.set_finger(dup_fp_finger) - - dup_path = self.get_print_name_file_path(dup_username, dup_finger) - os.makedirs(os.path.dirname(dup_path), exist_ok=True) - with open(dup_path, mode='wb') as new_print_file: - new_print_file.write(dup_print.serialize()) - print('Created ',dup_username,'duplicated',dup_finger, - 'print in', dup_path) - - self.assertFingerInStorage(dup_username, dup_fp_finger) - else: - self.assertCountEqual(enrolled_prints, set(enrolled_prints)) - - for user in enroll_map: - enrolled_fingers = enroll_map[user].keys() - if enrolled_fingers: - enrolled = self.device.ListEnrolledFingers('(s)', user) - self.assertCountEqual(enrolled_fingers, enrolled) - else: - with self.assertFprintError('NoEnrolledPrints'): - self.device.ListEnrolledFingers('(s)', user) - - return (enroll_map, enrolled_prints_info) - - def get_secondary_bus_and_device(self, claim=None): - addr = os.environ['DBUS_SYSTEM_BUS_ADDRESS'] - - # Get a separate bus connection - bus = Gio.DBusConnection.new_for_address_sync(addr, - Gio.DBusConnectionFlags.MESSAGE_BUS_CONNECTION | - Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT, None, None) - assert bus.is_closed() == False - - dev_path = self.device.get_object_path() - dev = Gio.DBusProxy.new_sync(bus, - Gio.DBusProxyFlags.DO_NOT_AUTO_START, - None, - FPRINT_NAMESPACE, - dev_path, - FPRINT_NAMESPACE + '.Device', - None) - - if claim is not None: - dev.Claim('(s)', claim) - - return bus, dev - - def assertVerifyMatch(self, selected_finger=None): - self.wait_for_result(expected='verify-match') - self.assertTrue(self._verify_stopped) - - if selected_finger: - self.assertEqual(selected_finger, self._selected_finger) - - def assertVerifyNoMatch(self, selected_finger=None): - self.wait_for_result(expected='verify-no-match') - self.assertTrue(self._verify_stopped) - - if selected_finger: - self.assertEqual(selected_finger, self._selected_finger) - - -class FPrintdVirtualStorageDeviceBaseTest(FPrintdVirtualDeviceBaseTest): - - socket_env = 'FP_VIRTUAL_DEVICE_STORAGE' - device_driver = 'virtual_device_storage' - driver_name = 'Virtual device with storage and identification for debugging' - enroll_stages = 2 - - def _send_command(self, con, command, *args): - params = ' '.join(str(p) for p in args) - con.sendall('{} {}'.format(command, params).encode('utf-8')) - res = [] - while True: - r = con.recv(1024) - if not r: - break - res.append(r) - - return b''.join(res) - - def send_command(self, command, *args): - self.assertIn(command, ['INSERT', 'REMOVE', 'SCAN', 'ERROR', 'RETRY', - 'FINGER', 'UNPLUG', 'SLEEP', 'SET_ENROLL_STAGES', 'SET_SCAN_TYPE', - 'SET_CANCELLATION_ENABLED', 'LIST', 'IGNORED_COMMAND', - 'SET_KEEP_ALIVE', 'CONT']) - - with Connection(self.sockaddr) as con: - res = self._send_command(con, command, *args) - - return res - - def send_image(self, image, con=None): - # This is meant to simulate the image scanning for image device - self.send_command('SCAN', image) - - def send_error(self, error=FPrint.DeviceError.GENERAL, con=None): - self.send_command('ERROR', int(error)) - - def send_retry(self, retry_error=FPrint.DeviceRetry.TOO_SHORT, con=None): - self.send_command('RETRY', int(retry_error)) - - def send_remove(self, con=None): - self.send_command('UNPLUG') - - def send_finger_automatic(self, automatic, con=None, iterate=True): - if not automatic: - return - self.skipTest('Not implemented for {}'.format(self.device_driver)) - - def send_finger_report(self, has_finger, con=None, iterate=True): - self.send_command('FINGER', 1 if has_finger else 0) - - while iterate and self.finger_present != has_finger: - ctx.iteration(False) - - def send_sleep(self, timeout, con=None): - self.assertGreaterEqual(timeout, 0) - self.send_command('SLEEP', timeout) - - def set_keep_alive(self, value): - self.send_command('SET_KEEP_ALIVE', 1 if value else 0) - - def enroll_print(self, nick, finger='right-index-finger', expected_result='enroll-completed'): - # Using the name of the image as the print id - super().enroll_image(img=nick, finger=finger, expected_result=expected_result) - - def _maybe_reduce_enroll_stages(self, stages=-1): - # Reduce the number of default enroll stages, we can go a bit faster - stages = stages if stages > 0 else self.enroll_stages - if self.has_identification: - stages += 1 # Adding the extra stage for duplicates-check - if self.num_enroll_stages == stages: - return - device_stages = stages -1 if self.has_identification else stages - self.send_command('SET_ENROLL_STAGES', device_stages) - while self.num_enroll_stages != stages: - ctx.iteration(True) - self.assertIn({'num-enroll-stages': stages}, self._changed_properties) - self._changed_properties.remove({'num-enroll-stages': stages}) - self.assertEqual(self.num_enroll_stages, stages) - - def get_stored_prints(self): - return self.send_command('LIST').decode('ascii').split('\n')[:-1] - - -class FPrintdVirtualStorageDeviceTests(FPrintdVirtualStorageDeviceBaseTest): - def setUp(self): - super().setUp() - self.device.Claim('(s)', 'testuser') - - def tearDown(self): - self.try_release() - super().tearDown() - - def test_garbage_collect(self): - # We expect collection in this order - garbage_collect = [ - 'no-metadata-print', - 'FP1-20201216-7-ABCDEFGH-testuser', - 'FP1-20201217-7-12345678-testuser', - ] - for e in garbage_collect: - self.send_command('INSERT', e) - # Enroll a few prints that must not be touched, sort them in at various points - enrolled_prints = { - 'FP1-20000101-7-ABCDEFGH-testuser' : 'left-index-finger', - 'FP1-20201231-7-ABCDEFGH-testuser' : 'right-index-finger', - 'no-metadata-new' : 'left-middle-finger', - } - # Device supports listing, so no initial cleanup - for i, f in enrolled_prints.items(): - self.enroll_print(i, f) - - # The virtual device sends a trailing \n - prints = self.get_stored_prints() - self.assertEqual(set(prints), set(garbage_collect + list(enrolled_prints.keys()))) - - def trigger_garbagecollect(): - self.send_image('some-other-print') - self.send_command('ERROR', int(FPrint.DeviceError.DATA_FULL)) - self.device.EnrollStart('(s)', 'right-thumb') - self.device.EnrollStop() - - trigger_garbagecollect() - - prints = self.get_stored_prints() - garbage_collect.pop() - self.assertEqual(set(prints), set(garbage_collect + list(enrolled_prints.keys()))) - - trigger_garbagecollect() - - prints = self.get_stored_prints() - garbage_collect.pop() - self.assertEqual(set(prints), set(garbage_collect + list(enrolled_prints.keys()))) - - trigger_garbagecollect() - - prints = self.get_stored_prints() - garbage_collect.pop() - self.assertEqual(set(prints), set(garbage_collect + list(enrolled_prints.keys()))) - - def test_garbage_collect_on_duplicate(self): - self._maybe_reduce_enroll_stages(stages=1) - self.send_command('INSERT', 'stored-print') - self.device.Release() - self.device.Claim('(s)', 'testuser') - - self.assertEqual(self.get_stored_prints(), ['stored-print']) - # Device supports listing, so no initial cleanup - self.device.EnrollStart('(s)', 'right-thumb') - self.send_image('stored-print') # During identify - self.wait_for_result('enroll-stage-passed') - self.assertFalse(self.get_stored_prints()) - - self.send_image('stored-print') - self.wait_for_result('enroll-completed') # During enroll - self.assertEqual(self.get_stored_prints(), ['stored-print']) - self.device.EnrollStop() - - def test_garbage_collect_failed_on_duplicate(self): - self._maybe_reduce_enroll_stages(stages=1) - self.send_command('INSERT', 'stored-print') - self.device.Release() - self.device.Claim('(s)', 'testuser') - - self.assertEqual(self.get_stored_prints(), ['stored-print']) - # Device supports listing, so no initial cleanup - self.device.EnrollStart('(s)', 'right-thumb') - self.send_image('stored-print') # During identify - self.send_error(FPrint.DeviceError.PROTO) # During garbage collecting - self.wait_for_result('enroll-duplicate') - self.assertEqual(self.get_stored_prints(), ['stored-print']) - - self.device.EnrollStop() - - def test_delete(self): - # We expect collection in this order - garbage_prints = [ - 'no-metadata-print', - 'FP1-20201216-7-ABCDEFGH-testuser', - 'FP1-20201217-7-12345678-testuser', - 'FP1-20201216-7-ABCDEFGH-other', - 'FP1-20201217-7-12345678-other', - ] - for e in garbage_prints: - self.send_command('INSERT', e) - # Enroll a few prints that will be deleted - enrolled_prints = { - 'FP1-20000101-7-ABCDEFGH-testuser' : 'left-index-finger', - 'FP1-20201231-7-ABCDEFGH-testuser' : 'right-index-finger', - 'no-metadata-new' : 'left-middle-finger', - } - for i, f in enrolled_prints.items(): - self.enroll_print(i, f) - - # The virtual device sends a trailing \n - prints = self.get_stored_prints() - self.assertEqual(set(prints), set(garbage_prints + list(enrolled_prints.keys()))) - - # Now, delete all prints for the user - self.device.DeleteEnrolledFingers2() - - # And verify they are all gone - prints = self.get_stored_prints() - self.assertEqual(set(prints), set(garbage_prints)) - - def test_local_storage_cleanup_data_error(self): - # Enroll a print and delete it - self.enroll_print('deleted-print', finger='left-thumb') - self.send_command('REMOVE', 'deleted-print') - - # Note: would be thrown anyway by the storage device if we scan something - self.send_error(FPrint.DeviceError.DATA_NOT_FOUND) - self.device.VerifyStart('(s)', 'any') - - self.wait_for_result('verify-no-match') - self.device.VerifyStop() - - # At this point, there is no print left - with self.assertFprintError('NoEnrolledPrints'): - self.device.ListEnrolledFingers('(s)', 'testuser') - - def test_local_storage_cleanup_no_match(self): - # Enroll a print and delete it - self.enroll_print('existing-print', finger='right-index-finger') - self.enroll_print('deleted-print', finger='left-thumb') - self.send_command('REMOVE', 'deleted-print') - - # We need to send a print that is known to the device - self.send_image('other-print') - self.device.VerifyStart('(s)', 'right-index-finger') - - self.wait_for_result('verify-no-match') - self.device.VerifyStop() - - # At this point, the deleted print has disappeared - self.assertEqual(set(self.device.ListEnrolledFingers('(s)', 'testuser')), {'right-index-finger'}) - - # Now, do the same thing, and the print will not be deleted - self.enroll_print('deleted-print', finger='left-thumb') - self.send_command('REMOVE', 'deleted-print') - - self.send_image('other-print') - self.device.VerifyStart('(s)', 'right-index-finger') - - self.wait_for_result('verify-no-match') - self.device.VerifyStop() - - # At this point, the deleted print is still there - self.assertEqual(set(self.device.ListEnrolledFingers('(s)', 'testuser')), {'right-index-finger', 'left-thumb'}) - - def test_enroll_with_one_stage_only(self): - self._maybe_reduce_enroll_stages(stages=1) - - self.enroll_print('FP1-20000101-7-ABCDEFGH-testuser', 'left-index-finger') - self.assertEqual(self.device.ListEnrolledFingers('(s)', 'testuser'), ['left-index-finger']) - - def test_scan_type_changes(self): - for scan_type in [FPrint.ScanType.PRESS, FPrint.ScanType.SWIPE]: - scan_type = scan_type.value_nick - self.send_command('SET_SCAN_TYPE', scan_type) - while self.device.get_cached_property('scan-type').unpack() != scan_type: - ctx.iteration(True) - self.assertIn({'scan-type': scan_type}, self._changed_properties) - self.assertEqual(self.device.get_cached_property('scan-type').unpack(), scan_type) - -class FPrintdVirtualStorageNoListDeviceTests(FPrintdVirtualStorageDeviceBaseTest): - - socket_env = 'FP_VIRTUAL_DEVICE_STORAGE_NO_LIST' - - def setUp(self): - super().setUp() - self.device.Claim('(s)', 'testuser') - - def tearDown(self): - self.try_release() - super().tearDown() - - def test_clear_storage(self): - # We expect collection in this order - garbage_collect = [ - 'no-metadata-print', - 'FP1-20201216-7-ABCDEFGH-testuser', - 'FP1-20201217-7-12345678-testuser', - ] - for e in garbage_collect: - self.send_command('INSERT', e) - - # Enroll print, return OK for storage clearing - self.send_command('CONT', 0) - self.enroll_print('print-1', 'left-index-finger') - - prints = self.get_stored_prints() - self.assertEqual(set(prints), {'print-1'}) - - self.enroll_print('print-2', 'right-index-finger') - - prints = self.get_stored_prints() - self.assertEqual(set(prints), {'print-1', 'print-2'}) - - -class FPrintdVirtualNoStorageDeviceBaseTest(FPrintdVirtualStorageDeviceBaseTest): - - socket_env = 'FP_VIRTUAL_DEVICE' - device_driver = 'virtual_device' - driver_name = 'Virtual device for debugging' - has_identification = False - - -class FPrintdVirtualNoStorageDeviceTest(FPrintdVirtualNoStorageDeviceBaseTest): - - def check_verify_finger_match(self, image, expect_match, finger): - self.device.VerifyStart('(s)', 'any') - self.send_image(image) - if expect_match: - self.assertVerifyMatch(selected_finger=finger) - else: - self.assertVerifyNoMatch(selected_finger=finger) - self.device.VerifyStop() - - def test_verify_any_finger_match_first_only(self): - self.device.Claim('(s)', 'testuser') - self.addCleanup(self.device.Release) - - enrolled, enroll_map = self.enroll_multiple_images() - self.check_verify_finger_match(enroll_map[enrolled[0]], expect_match=True, - finger=enrolled[0]) - self.check_verify_finger_match(enroll_map[enrolled[1]], expect_match=False, - finger=enrolled[0]) - self.check_verify_finger_match(enroll_map[enrolled[2]], expect_match=False, - finger=enrolled[0]) - - def test_verify_any_finger_no_match(self): - self.device.Claim('(s)', 'testuser') - self.addCleanup(self.device.Release) - - FPrintdVirtualDeviceClaimedTest.test_verify_any_finger_no_match(self, - selected_finger=None) - - -class FPrintdManagerTests(FPrintdVirtualDeviceBaseTest): - - def setUp(self): - super().setUp() - self._polkitd_obj.SetAllowed(['']) - - def test_manager_get_devices(self): - self.assertListEqual(self.manager.GetDevices(), - [ self.device.get_object_path() ]) - - def test_manager_get_default_device(self): - self.assertEqual(self.manager.GetDefaultDevice(), - self.device.get_object_path()) - - -class FPrintdManagerPreStartTests(FPrintdVirtualImageDeviceBaseTests): - - def test_manager_get_no_devices(self): - os.environ['FP_DRIVERS_WHITELIST'] = 'hopefully_no_existing_driver' - self.daemon_start() - self.assertListEqual(self.manager.GetDevices(), []) - - def test_manager_get_no_default_device(self): - os.environ['FP_DRIVERS_WHITELIST'] = 'hopefully_no_existing_driver' - self.daemon_start() - - with self.assertFprintError('NoSuchDevice'): - self.manager.GetDefaultDevice() - - def test_manager_get_devices_on_name_appeared(self): - self._appeared_name = None - - def on_name_appeared(connection, name, name_owner): - self._appeared_name = name - - def on_name_vanished(connection, name): - self._appeared_name = 'NAME_VANISHED' - - id = Gio.bus_watch_name_on_connection(self.dbus, - FPRINT_NAMESPACE, Gio.BusNameWatcherFlags.NONE, - on_name_appeared, on_name_vanished) - self.addCleanup(Gio.bus_unwatch_name, id) - - self.daemon_start() - while not self._appeared_name: - ctx.iteration(True) - - self.assertEqual(self._appeared_name, FPRINT_NAMESPACE) - - try: - appeared_device = self.dbus.call_sync( - FPRINT_NAMESPACE, - FPRINT_PATH + '/Manager', - FPRINT_NAMESPACE + '.Manager', - 'GetDefaultDevice', None, None, - Gio.DBusCallFlags.NO_AUTO_START, 500, None) - except GLib.GError as e: - if FPRINT_NAMESPACE + '.Error.NoSuchDevice' in e.message: - self.skipTest("Need virtual_image device to run the test") - raise(e) - - self.assertIsNotNone(appeared_device) - [dev_path] = appeared_device - self.assertTrue(dev_path.startswith(FPRINT_PATH + '/Device/')) - - -class FPrintdVirtualDeviceTest(FPrintdVirtualDeviceBaseTest): - - def test_name_property(self): - self.assertEqual(self.device.get_cached_property('name').unpack(), - self.driver_name) - - def test_enroll_stages_property(self): - self.assertEqual(self.device.get_cached_property('num-enroll-stages').unpack(), 6) - - def test_scan_type(self): - self.assertEqual(self.device.get_cached_property('scan-type').unpack(), - 'swipe') - - def test_initial_finger_needed(self): - self.assertFalse(self.finger_needed) - - def test_initial_finger_needed(self): - self.assertFalse(self.finger_present) - - def test_allowed_claim_release_enroll(self): - self._polkitd_obj.SetAllowed([FprintDevicePermission.set_username, - FprintDevicePermission.enroll]) - self.device.Claim('(s)', 'testuser') - self.device.Release() - - def test_allowed_claim_release_verify(self): - self._polkitd_obj.SetAllowed([FprintDevicePermission.set_username, - FprintDevicePermission.verify]) - self.device.Claim('(s)', 'testuser') - self.device.Release() - - def test_allowed_claim_current_user(self): - self._polkitd_obj.SetAllowed([FprintDevicePermission.enroll]) - self.device.Claim('(s)', '') - self.device.Release() - - self.device.Claim('(s)', self.get_current_user()) - self.device.Release() - - def test_allowed_list_enrolled_fingers_empty_user(self): - self._polkitd_obj.SetAllowed([FprintDevicePermission.enroll]) - self.device.Claim('(s)', '') - self.enroll_image('whorl', finger='left-thumb') - - self._polkitd_obj.SetAllowed([FprintDevicePermission.verify]) - - self.assertEqual(self.device.ListEnrolledFingers('(s)', ''), ['left-thumb']) - self.assertEqual(self.device.ListEnrolledFingers('(s)', self.get_current_user()), ['left-thumb']) - - def test_allowed_list_enrolled_fingers_current_user(self): - self._polkitd_obj.SetAllowed([FprintDevicePermission.enroll]) - self.device.Claim('(s)', self.get_current_user()) - self.enroll_image('whorl', finger='right-thumb') - - self._polkitd_obj.SetAllowed([FprintDevicePermission.verify]) - - self.assertEqual(self.device.ListEnrolledFingers('(s)', ''), ['right-thumb']) - self.assertEqual(self.device.ListEnrolledFingers('(s)', self.get_current_user()), ['right-thumb']) - - def test_unallowed_claim(self): - self._polkitd_obj.SetAllowed(['']) - - with self.assertFprintError('PermissionDenied'): - self.device.Claim('(s)', 'testuser') - - self._polkitd_obj.SetAllowed([FprintDevicePermission.set_username]) - - with self.assertFprintError('PermissionDenied'): - self.device.Claim('(s)', 'testuser') - - self._polkitd_obj.SetAllowed([FprintDevicePermission.enroll]) - - with self.assertFprintError('PermissionDenied'): - self.device.Claim('(s)', 'testuser') - - self._polkitd_obj.SetAllowed([FprintDevicePermission.verify]) - - with self.assertFprintError('PermissionDenied'): - self.device.Claim('(s)', 'testuser') - - def test_unallowed_enroll_with_verify_claim(self): - self._polkitd_obj.SetAllowed([FprintDevicePermission.verify]) - self.device.Claim('(s)', '') - - with self.assertFprintError('PermissionDenied'): - self.enroll_image('whorl', finger='right-thumb') - - def test_unallowed_delete_with_verify_claim(self): - self._polkitd_obj.SetAllowed([FprintDevicePermission.verify]) - self.device.Claim('(s)', '') - - with self.assertFprintError('PermissionDenied'): - self.device.DeleteEnrolledFingers('(s)', 'testuser') - - def test_unallowed_delete2_with_verify_claim(self): - self._polkitd_obj.SetAllowed([FprintDevicePermission.verify]) - self.device.Claim('(s)', '') - - with self.assertFprintError('PermissionDenied'): - self.device.DeleteEnrolledFingers2() - - def test_unallowed_delete_single_with_verify_claim(self): - self._polkitd_obj.SetAllowed([FprintDevicePermission.verify]) - self.device.Claim('(s)', '') - - with self.assertFprintError('PermissionDenied'): - self.device.DeleteEnrolledFingers('(s)', 'right-thumb') - - def test_unallowed_verify_with_enroll_claim(self): - self._polkitd_obj.SetAllowed([FprintDevicePermission.enroll]) - self.device.Claim('(s)', '') - - with self.assertFprintError('PermissionDenied'): - self.device.VerifyStart('(s)', 'any') - - def test_unallowed_claim_current_user(self): - self._polkitd_obj.SetAllowed(['']) - - with self.assertFprintError('PermissionDenied'): - self.device.Claim('(s)', '') - - with self.assertFprintError('PermissionDenied'): - self.device.Claim('(s)', self.get_current_user()) - - def test_multiple_claims(self): - self.device.Claim('(s)', 'testuser') - - with self.assertFprintError('AlreadyInUse'): - self.device.Claim('(s)', 'testuser') - - self.device.Release() - - def test_always_allowed_release(self): - self.device.Claim('(s)', 'testuser') - - self._polkitd_obj.SetAllowed(['']) - - self.device.Release() - - def test_unclaimed_release(self): - with self.assertFprintError('ClaimDevice'): - self.device.Release() - - def test_unclaimed_verify_start(self): - with self.assertFprintError('ClaimDevice'): - self.device.VerifyStart('(s)', 'any') - - def test_unclaimed_verify_stop(self): - with self.assertFprintError('ClaimDevice'): - self.device.VerifyStop() - - def test_unclaimed_enroll_start(self): - with self.assertFprintError('ClaimDevice'): - self.device.EnrollStart('(s)', 'left-index-finger') - - def test_unclaimed_enroll_stop(self): - with self.assertFprintError('ClaimDevice'): - self.device.EnrollStop() - - def test_unclaimed_delete_enrolled_fingers(self): - self.enroll_image('whorl', claim_user='foo-user') - self.device.DeleteEnrolledFingers('(s)', 'foo-user') - - def test_unclaimed_delete_enrolled_fingers_no_prints(self): - with self.assertFprintError('NoEnrolledPrints'): - self.device.DeleteEnrolledFingers('(s)', 'testuser') - - def test_unclaimed_delete_enrolled_finger(self): - with self.assertFprintError('ClaimDevice'): - self.device.DeleteEnrolledFinger('(s)', 'left-index-finger') - - def test_unclaimed_delete_enrolled_fingers2(self): - with self.assertFprintError('ClaimDevice'): - self.device.DeleteEnrolledFingers2() - - def test_unclaimed_list_enrolled_fingers(self): - self.enroll_image('whorl', finger='left-thumb', claim_user='testuser') - self.assertEqual(self.device.ListEnrolledFingers('(s)', 'testuser'), - ['left-thumb']) - - def test_unclaimed_list_enrolled_fingers_error(self): - with self.assertFprintError('NoEnrolledPrints'): - self.device.ListEnrolledFingers('(s)', 'testuser') - - def test_unclaimed_list_enrolled_fingers_ignores_invalid(self): - print_path = self.get_print_file_path('testuser', FPrint.Finger.LEFT_INDEX) - os.makedirs(os.path.dirname(print_path), exist_ok=True) - with open(print_path, mode='wb') as new_print_file: - new_print_file.write(b'I am an invalid print!') - - with self.assertFprintError('NoEnrolledPrints'): - self.device.ListEnrolledFingers('(s)', 'testuser') - - def test_claim_device_open_fail(self): - os.rename(self.tmpdir, self.tmpdir + '-moved') - self.addCleanup(os.rename, self.tmpdir + '-moved', self.tmpdir) - - with self.assertFprintError('Internal'): - self.device.Claim('(s)', 'testuser') - - def test_claim_from_other_client_is_released_when_vanished(self): - self.call_device_method_from_other_client('Claim', ['testuser']) - time.sleep(1) - self.device.Claim('(s)', 'testuser') - self.device.Release() - - def test_claim_disconnect(self): - bus, dev = self.get_secondary_bus_and_device() - - def call_done(obj, result, user_data): - # Ignore the callback (should be an error) - pass - - # Do an async call to claim and immediately close - dev.Claim('(s)', 'testuser', result_handler=call_done) - - # Ensure the call is on the wire, then close immediately - bus.flush_sync() - bus.close_sync() - - time.sleep(1) - - def test_enroll_running_disconnect(self): - bus, dev = self.get_secondary_bus_and_device(claim='testuser') - - # Start an enroll and disconnect, without finishing/cancelling - dev.EnrollStart('(s)', 'left-index-finger') - - # Ensure the call is on the wire, then close immediately - bus.flush_sync() - bus.close_sync() - - time.sleep(1) - - def test_enroll_done_disconnect(self): - bus, dev = self.get_secondary_bus_and_device(claim='testuser') - - # Start an enroll and disconnect, without finishing/cancelling - dev.EnrollStart('(s)', 'left-index-finger') - - # This works because we also receive the signals on the main connection - stages = dev.get_cached_property('num-enroll-stages').unpack() - for stage in range(stages): - self.send_image('whorl') - if stage < stages - 1: - self.wait_for_result('enroll-stage-passed') - else: - self.wait_for_result('enroll-completed') - - bus.close_sync() - - time.sleep(1) - - def test_verify_running_disconnect(self): - bus, dev = self.get_secondary_bus_and_device(claim='testuser') - self.enroll_image('whorl', device=dev) - - # Start an enroll and disconnect, without finishing/cancelling - dev.VerifyStart('(s)', 'right-index-finger') - - bus.close_sync() - - time.sleep(1) - - def test_verify_done_disconnect(self): - bus, dev = self.get_secondary_bus_and_device(claim='testuser') - self.enroll_image('whorl', device=dev) - - # Start an enroll and disconnect, without finishing/cancelling - dev.VerifyStart('(s)', 'right-index-finger') - self.send_image('whorl') - # Wait for match and sleep a bit to give fprintd time to wrap up - self.wait_for_result('verify-match') - time.sleep(1) - - bus.close_sync() - - time.sleep(1) - - def test_identify_running_disconnect(self): - bus, dev = self.get_secondary_bus_and_device(claim='testuser') - self.enroll_image('whorl', device=dev) - - # Start an enroll and disconnect, without finishing/cancelling - dev.VerifyStart('(s)', 'any') - - bus.close_sync() - - time.sleep(1) - - def test_identify_done_disconnect(self): - bus, dev = self.get_secondary_bus_and_device(claim='testuser') - self.enroll_image('whorl', device=dev) - - # Start an enroll and disconnect, without finishing/cancelling - dev.VerifyStart('(s)', 'any') - self.send_image('whorl') - # Wait for match and sleep a bit to give fprintd time to wrap up - self.wait_for_result('verify-match') - time.sleep(1) - - bus.close_sync() - - time.sleep(1) - - def test_removal_during_enroll(self): - if not self._has_hotplug: - self.skipTest("libfprint is too old for hotplug") - - self._polkitd_obj.SetAllowed([FprintDevicePermission.set_username, - FprintDevicePermission.enroll]) - self.device.Claim('(s)', 'testuser') - self.device.EnrollStart('(s)', 'left-index-finger') - - # Now remove the device while we are enrolling, which will cause an error - self.send_remove() - self.wait_for_result(expected='enroll-unknown-error') - - # The device will still be there now until it is released - devices = self.manager.GetDevices() - self.assertIn(self.device.get_object_path(), devices) - with self.assertFprintError('Internal'): - self.device.Release() - - # And now it will be gone - devices = self.manager.GetDevices() - self.assertNotIn(self.device.get_object_path(), devices) - - def test_concourrent_claim(self): - self.call_device_method_async('Claim', '(s)', ['']) - self.call_device_method_async('Claim', '(s)', ['']) - - with self.assertFprintError('AlreadyInUse'): - self.wait_for_device_reply(expected_replies=2) - - self.assertIn(GLib.Variant('()', ()), self.get_all_async_replies()) - - def test_suspend_inhibit_unclaimed(self): - self.logind_obj.EmitSignal("", "PrepareForSleep", "b", [True]) - - self.daemon_log.check_line('Preparing devices for sleep', timeout=1) - self.wait_released_delay_inhibitor(timeout=1) - - self.logind_obj.EmitSignal("", "PrepareForSleep", "b", [False]) - - self.daemon_log.check_line('Preparing devices for resume', timeout=1) - self.wait_got_delay_inhibitor(timeout=1) - - def test_suspend_inhibit_claimed(self): - self.device.Claim('(s)', 'testuser') - - self.logind_obj.EmitSignal("", "PrepareForSleep", "b", [True]) - - self.daemon_log.check_line('Preparing devices for sleep', timeout=1) - self.wait_released_delay_inhibitor(timeout=1) - - self.logind_obj.EmitSignal("", "PrepareForSleep", "b", [False]) - - self.daemon_log.check_line('Preparing devices for resume', timeout=1) - self.wait_got_delay_inhibitor(timeout=1) - - self.device.Release() - - def test_suspend_inhibit_cancels_enroll(self): - self.device.Claim('(s)', 'testuser') - - self.device.EnrollStart('(s)', 'right-thumb') - - # Now prepare for sleep, which will trigger an internal cancellation - self.logind_obj.EmitSignal("", "PrepareForSleep", "b", [True]) - - self.daemon_log.check_line('Preparing devices for sleep', timeout=1) - self.wait_for_result(expected='enroll-unknown-error') - self.wait_released_delay_inhibitor(timeout=1) - - self.assertEqual(os.read(self.logind_inhibit_fifo, 1), b'') - - self.logind_obj.EmitSignal("", "PrepareForSleep", "b", [False]) - - self.daemon_log.check_line('Preparing devices for resume', timeout=1) - self.wait_got_delay_inhibitor(timeout=1) - - self.device.Release() - - def test_suspend_prevents_enroll(self): - self.device.Claim('(s)', 'testuser') - - # Now prepare for sleep, which will trigger an internal cancellation - self.logind_obj.EmitSignal("", "PrepareForSleep", "b", [True]) - - self.daemon_log.check_line('Preparing devices for sleep', timeout=1) - self.wait_released_delay_inhibitor(timeout=1) - - self.device.EnrollStart('(s)', 'right-thumb') - self.wait_for_result(expected='enroll-unknown-error') - - self.logind_obj.EmitSignal("", "PrepareForSleep", "b", [False]) - - self.daemon_log.check_line('Preparing devices for resume', timeout=1) - self.wait_got_delay_inhibitor(timeout=1) - - self.device.Release() - - -class FPrintdVirtualDeviceStorageTest(FPrintdVirtualStorageDeviceBaseTest, - FPrintdVirtualDeviceTest): - # Repeat the tests for the Virtual storage device - def test_claim_error(self): - self.device.Claim('(s)', self.get_current_user()) - self.addCleanup(self.try_release) - self.set_keep_alive(True) - self.device.Release() - - self.send_error(FPrint.DeviceError.PROTO) - with self.assertFprintError('Internal'): - self.device.Claim('(s)', 'testuser') - - -class FPrintdVirtualDeviceClaimedTest(FPrintdVirtualDeviceBaseTest): - - def setUp(self): - super().setUp() - self.device.Claim('(s)', 'testuser') - - def tearDown(self): - self._polkitd_obj.SetAllowed([FprintDevicePermission.enroll]) - self.try_release() - super().tearDown() - - def test_any_finger_enroll_start(self): - with self.assertFprintError('InvalidFingername'): - self.device.EnrollStart('(s)', 'any') - - def test_wrong_finger_enroll_start(self): - with self.assertFprintError('InvalidFingername'): - self.device.EnrollStart('(s)', 'sixth-right-finger') - - def test_any_finger_delete_print(self): - with self.assertFprintError('InvalidFingername'): - self.device.DeleteEnrolledFinger('(s)', 'any') - - def test_wrong_finger_delete_print(self): - with self.assertFprintError('InvalidFingername'): - self.device.DeleteEnrolledFinger('(s)', 'sixth-left-finger') - - def test_delete_with_no_enrolled_prints(self): - with self.assertFprintError('NoEnrolledPrints'): - self.device.DeleteEnrolledFinger('(s)', 'left-index-finger') - - def test_verify_with_no_enrolled_prints(self): - with self.assertFprintError('NoEnrolledPrints'): - self.device.VerifyStart('(s)', 'any') - - def test_enroll_verify_list_delete(self): - # This test can trigger a race in older libfprint, only run if we have - # hotplug support, which coincides with the fixed release. - if not self._has_hotplug: - self.skipTest("libfprint is too old for hotplug") - - with self.assertFprintError('NoEnrolledPrints'): - self.device.ListEnrolledFingers('(s)', 'testuser') - - with self.assertFprintError('NoEnrolledPrints'): - self.device.ListEnrolledFingers('(s)', 'nottestuser') - - self.enroll_image('whorl') - self.assertFingerInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - - with self.assertFprintError('NoEnrolledPrints'): - self.device.ListEnrolledFingers('(s)', 'nottestuser') - - self.assertEqual(self.device.ListEnrolledFingers('(s)', 'testuser'), ['right-index-finger']) - - # Finger is enrolled, try to verify it - self.device.VerifyStart('(s)', 'any') - - while not self.finger_needed: - ctx.iteration(True) - - self.assertTrue(self.finger_needed) - self.assertFalse(self.finger_present) - - # Try a wrong print; will stop verification - self.send_image('tented_arch') - self.assertVerifyNoMatch() - - self.device.VerifyStop() - self.device.VerifyStart('(s)', 'any') - - # Send a retry error (swipe too short); will not stop verification - self.send_retry() - self.wait_for_result() - self.assertFalse(self._verify_stopped) - self.assertEqual(self._last_result, 'verify-swipe-too-short') - - # Try the correct print; will stop verification - self.send_image('whorl') - self.wait_for_result() - self.assertTrue(self._verify_stopped) - self.assertEqual(self._last_result, 'verify-match') - self.device.VerifyStop() - - self.assertEqual(self.device.ListEnrolledFingers('(s)', 'testuser'), ['right-index-finger']) - - # And delete the print(s) again - self.device.DeleteEnrolledFingers('(s)', 'testuser') - - self.assertFingerNotInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - - with self.assertFprintError('NoEnrolledPrints'): - self.device.ListEnrolledFingers('(s)', 'testuser') - - def test_enroll_delete_storage_error(self): - self.enroll_image('whorl') - self.enroll_image('tented_arch', finger='left-index-finger') - - self.set_print_not_writable('testuser', FPrint.Finger.RIGHT_INDEX) - - self.assertFingerInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - self.assertFingerInStorage('testuser', FPrint.Finger.LEFT_INDEX) - - with self.assertFprintError('PrintsNotDeleted'): - self.device.DeleteEnrolledFingers('(s)', 'testuser') - - self.assertFingerInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - self.assertFingerNotInStorage('testuser', FPrint.Finger.LEFT_INDEX) - - def test_enroll_delete2(self): - self.enroll_image('whorl') - - self.assertFingerInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - - # And delete the print(s) again using the new API - self.device.DeleteEnrolledFingers2() - - self.assertFingerNotInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - self.assertFalse(os.path.exists(os.path.join(self.state_dir, 'testuser'))) - self.assertTrue(os.path.exists(self.state_dir)) - - def test_enroll_delete2_multiple(self): - self.enroll_image('whorl') - self.enroll_image('tented_arch', finger='left-index-finger') - - self.assertFingerInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - self.assertFingerInStorage('testuser', FPrint.Finger.LEFT_INDEX) - - self.device.DeleteEnrolledFingers2() - - self.assertFingerNotInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - self.assertFingerNotInStorage('testuser', FPrint.Finger.LEFT_INDEX) - - def test_enroll_delete2_storage_error(self): - self.enroll_image('whorl') - self.enroll_image('tented_arch', finger='left-index-finger') - - self.set_print_not_writable('testuser', FPrint.Finger.RIGHT_INDEX) - - self.assertFingerInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - self.assertFingerInStorage('testuser', FPrint.Finger.LEFT_INDEX) - - with self.assertFprintError('PrintsNotDeleted'): - self.device.DeleteEnrolledFingers2() - - self.assertFingerInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - self.assertFingerNotInStorage('testuser', FPrint.Finger.LEFT_INDEX) - - def test_enroll_delete_single(self): - self.enroll_image('whorl', finger='right-index-finger') - self.enroll_image('tented_arch', finger='left-index-finger') - - self.assertFingerInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - self.assertFingerInStorage('testuser', FPrint.Finger.LEFT_INDEX) - - self.device.DeleteEnrolledFinger('(s)', 'right-index-finger') - self.assertFingerInStorage('testuser', FPrint.Finger.LEFT_INDEX) - self.assertFingerNotInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - - self.device.DeleteEnrolledFinger('(s)', 'left-index-finger') - self.assertFingerNotInStorage('testuser', FPrint.Finger.LEFT_INDEX) - self.assertFingerNotInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - - def test_enroll_delete_single_storage_error(self): - self.enroll_image('whorl', finger='right-index-finger') - self.enroll_image('tented_arch', finger='left-index-finger') - - self.set_print_not_writable('testuser', FPrint.Finger.RIGHT_INDEX) - - self.assertFingerInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - self.assertFingerInStorage('testuser', FPrint.Finger.LEFT_INDEX) - - with self.assertFprintError('PrintsNotDeleted'): - self.device.DeleteEnrolledFinger('(s)', 'right-index-finger') - - self.assertFingerInStorage('testuser', FPrint.Finger.LEFT_INDEX) - self.assertFingerInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - - self.set_print_not_writable('testuser', FPrint.Finger.LEFT_INDEX) - - with self.assertFprintError('PrintsNotDeleted'): - self.device.DeleteEnrolledFinger('(s)', 'left-index-finger') - - self.assertFingerInStorage('testuser', FPrint.Finger.LEFT_INDEX) - self.assertFingerInStorage('testuser', FPrint.Finger.RIGHT_INDEX) - - def test_enroll_invalid_storage_dir(self): - # Directory will not exist yet - os.makedirs(self.state_dir, mode=0o500) - self.addCleanup(os.chmod, self.state_dir, mode=0o700) - - self.skipTestIfCanWrite(self.state_dir) - - self.enroll_image('whorl', expected_result='enroll-failed') - - def test_enroll_write_print_error(self): - self.set_print_not_writable('testuser', FPrint.Finger.LEFT_THUMB) - self.enroll_image('whorl', expected_result='enroll-failed', finger='left-thumb') - - def test_verify_invalid_storage_dir(self): - self.enroll_image('whorl') - os.chmod(self.state_dir, mode=0o000) - self.addCleanup(os.chmod, self.state_dir, mode=0o700) - - self.skipTestIfCanWrite(self.state_dir) - - with self.assertFprintError('NoEnrolledPrints'): - self.device.VerifyStart('(s)', 'any') - - def test_verify_read_print_error(self): - self.enroll_image('whorl', finger='left-thumb') - self.set_print_not_writable('testuser', FPrint.Finger.LEFT_THUMB) - - with self.assertFprintError('NoEnrolledPrints'): - self.device.VerifyStart('(s)', 'any') - - def test_enroll_stop_cancels(self): - self.device.EnrollStart('(s)', 'left-index-finger') - self.device.EnrollStop() - self.wait_for_result(expected='enroll-failed') - - def test_verify_stop_cancels(self): - self.enroll_image('whorl') - self.device.VerifyStart('(s)', 'any') - self.device.VerifyStop() - self.wait_for_result(expected='verify-no-match') - - def test_verify_finger_stop_cancels(self): - self.enroll_image('whorl', finger='left-thumb') - self.device.VerifyStart('(s)', 'left-thumb') - self.device.VerifyStop() - - def test_busy_device_release_on_enroll(self): - self.device.EnrollStart('(s)', 'left-index-finger') - - self.device.Release() - self.wait_for_result(expected='enroll-failed') - - def test_busy_device_release_on_verify(self): - self.enroll_image('whorl', finger='left-index-finger') - self.device.VerifyStart('(s)', 'any') - - self.device.Release() - self.wait_for_result(expected='verify-no-match') - - def test_busy_device_release_on_verify_finger(self): - self.enroll_image('whorl', finger='left-middle-finger') - self.device.VerifyStart('(s)', 'left-middle-finger') - - self.device.Release() - self.wait_for_result(expected='verify-no-match') - - def test_enroll_stop_not_started(self): - with self.assertFprintError('NoActionInProgress'): - self.device.EnrollStop() - - def test_verify_stop_not_started(self): - with self.assertFprintError('NoActionInProgress'): - self.device.VerifyStop() - - def test_verify_finger_match(self): - self.enroll_image('whorl', finger='left-thumb') - self.device.VerifyStart('(s)', 'left-thumb') - self.send_image('whorl') - self.wait_for_result() - self.assertTrue(self._verify_stopped) - self.assertEqual(self._last_result, 'verify-match') - self.assertEqual(self._selected_finger, 'left-thumb') - self.device.VerifyStop() - - def test_verify_finger_no_match(self): - self.enroll_image('whorl', finger='left-thumb') - self.device.VerifyStart('(s)', 'left-thumb') - self.send_image('tented_arch') - self.assertVerifyNoMatch(selected_finger='left-thumb') - self.device.VerifyStop() - - def test_verify_finger_no_match_restart(self): - self.enroll_image('whorl', finger='left-thumb') - self.device.VerifyStart('(s)', 'left-thumb') - self.send_image('tented_arch') - self.assertVerifyNoMatch(selected_finger='left-thumb') - self.device.VerifyStop() - - # Immediately starting again after a no-match must work - self.device.VerifyStart('(s)', 'left-thumb') - self.send_image('whorl') - self.wait_for_result() - self.assertTrue(self._verify_stopped) - self.assertEqual(self._last_result, 'verify-match') - self.assertEqual(self._selected_finger, 'left-thumb') - self.device.VerifyStop() - - def test_verify_wrong_finger_match(self): - self.enroll_image('whorl', finger='left-thumb') - self.device.VerifyStart('(s)', 'left-toe') - self.send_image('whorl') - self.wait_for_result() - self.assertTrue(self._verify_stopped) - self.assertEqual(self._last_result, 'verify-match') - self.assertEqual(self._selected_finger, 'left-thumb') - self.device.VerifyStop() - - def test_verify_wrong_finger_no_match(self): - self.enroll_image('whorl', finger='right-thumb') - self.device.VerifyStart('(s)', 'right-toe') - self.send_image('tented_arch') - self.assertVerifyNoMatch(selected_finger='right-thumb') - self.device.VerifyStop() - - def test_verify_any_finger_match(self): - second_image = self.enroll_multiple_images(return_index=1) - self.device.VerifyStart('(s)', 'any') - self.send_image(second_image) - self.wait_for_result() - self.assertTrue(self._verify_stopped) - self.assertEqual(self._last_result, 'verify-match') - self.assertEqual(self._selected_finger, 'any') - self.device.VerifyStop() - - def test_verify_any_finger_no_match(self, selected_finger='any'): - enrolled, _map = self.enroll_multiple_images() - verify_image = 'tented_arch' - self.assertNotIn(verify_image, enrolled) - self.device.VerifyStart('(s)', 'any') - self.send_image(verify_image) - self.assertVerifyNoMatch(selected_finger) - self.device.VerifyStop() - - def test_verify_any_finger_multiple_users(self): - enroll_map, enrolled_prints_info = self.enroll_users_images() - enrolled_users = list(enroll_map) - - for verifying_user in enrolled_users: - self.device.Claim('(s)', verifying_user) - - for enrolled_user in enrolled_users: - should_match = enrolled_user == verifying_user - - for finger, print in enroll_map[enrolled_user].items(): - self.device.VerifyStart('(s)', 'any') - self.send_image(print) - if should_match: - self.assertVerifyMatch() - else: - self.assertVerifyNoMatch() - self.device.VerifyStop() - - self.device.Release() - - def test_enroll_users_duplicate_prints(self): - _enroll_map, prints_info = self.enroll_users_images(enroll_map={ - 'test-user1': {'left-thumb': 'whorl', 'right-thumb': 'whorl'}, - 'test-user2': {'left-index-finger': 'whorl'}, - 'test-user3': {'left-little-finger': 'tented_arch'}, - }, allow_duplicates=True) - self.assertEqual(prints_info, { - 'whorl': ('test-user1', 'left-thumb'), - 'tented_arch': ('test-user3', 'left-little-finger'), - }) - - def test_verify_finger_not_enrolled(self): - self.enroll_image('whorl', finger='left-thumb') - with self.assertFprintError('NoEnrolledPrints'): - self.device.VerifyStart('(s)', 'right-thumb') - - def test_verify_finger_not_enrolled_stops_verification(self): - self.enroll_image('whorl', finger='left-thumb') - with self.assertFprintError('NoEnrolledPrints'): - self.device.VerifyStart('(s)', 'right-thumb') - - with self.assertFprintError('NoActionInProgress'): - self.device.VerifyStop() - - def test_identify_finger_not_enrolled(self): - with self.assertFprintError('NoEnrolledPrints'): - self.device.VerifyStart('(s)', 'any') - - def test_identify_finger_not_enrolled_stops_verification(self): - with self.assertFprintError('NoEnrolledPrints'): - self.device.VerifyStart('(s)', 'any') - - with self.assertFprintError('NoActionInProgress'): - self.device.VerifyStop() - - def test_unallowed_enroll_start(self): - self._polkitd_obj.SetAllowed(['']) - - with self.assertFprintError('PermissionDenied'): - self.device.EnrollStart('(s)', 'right-index-finger') - - self._polkitd_obj.SetAllowed([FprintDevicePermission.enroll]) - self.enroll_image('whorl') - - def test_always_allowed_enroll_stop(self): - self.device.EnrollStart('(s)', 'right-index-finger') - - self._polkitd_obj.SetAllowed(['']) - - self.device.EnrollStop() - - def test_unallowed_verify_start(self): - self._polkitd_obj.SetAllowed(['']) - - with self.assertFprintError('PermissionDenied'): - self.device.VerifyStart('(s)', 'any') - - def test_always_allowed_verify_stop(self): - self.enroll_image('whorl') - self.device.VerifyStart('(s)', 'any') - - self._polkitd_obj.SetAllowed(['']) - self.device.VerifyStop() - - def test_list_enrolled_fingers_current_user(self): - self.enroll_image('whorl') - self._polkitd_obj.SetAllowed([FprintDevicePermission.verify]) - - with self.assertFprintError('NoEnrolledPrints'): - self.device.ListEnrolledFingers('(s)', '') - - with self.assertFprintError('NoEnrolledPrints'): - self.device.ListEnrolledFingers('(s)', self.get_current_user()) - - def test_unallowed_list_enrolled_fingers(self): - self.enroll_image('whorl') - - self._polkitd_obj.SetAllowed(['']) - with self.assertFprintError('PermissionDenied'): - self.device.ListEnrolledFingers('(s)', 'testuser') - - self._polkitd_obj.SetAllowed([FprintDevicePermission.set_username]) - with self.assertFprintError('PermissionDenied'): - self.device.ListEnrolledFingers('(s)', 'testuser') - - def test_unallowed_list_enrolled_fingers_current_user(self): - self.enroll_image('whorl') - - self._polkitd_obj.SetAllowed(['']) - with self.assertFprintError('PermissionDenied'): - self.device.ListEnrolledFingers('(s)', '') - - with self.assertFprintError('PermissionDenied'): - self.device.ListEnrolledFingers('(s)', self.get_current_user()) - - self._polkitd_obj.SetAllowed([FprintDevicePermission.set_username]) - with self.assertFprintError('PermissionDenied'): - self.device.ListEnrolledFingers('(s)', '') - - with self.assertFprintError('PermissionDenied'): - self.device.ListEnrolledFingers('(s)', self.get_current_user()) - - def test_unallowed_delete_enrolled_fingers(self): - self.enroll_image('whorl') - - self._polkitd_obj.SetAllowed(['']) - with self.assertFprintError('PermissionDenied'): - self.device.DeleteEnrolledFingers('(s)', 'testuser') - - self._polkitd_obj.SetAllowed([FprintDevicePermission.set_username]) - with self.assertFprintError('PermissionDenied'): - self.device.DeleteEnrolledFingers('(s)', 'testuser') - - def test_unallowed_delete_enrolled_fingers2(self): - self.enroll_image('whorl') - - self._polkitd_obj.SetAllowed(['']) - with self.assertFprintError('PermissionDenied'): - self.device.DeleteEnrolledFingers2() - - def test_unallowed_delete_enrolled_finger(self): - self.enroll_image('whorl') - - self._polkitd_obj.SetAllowed(['']) - with self.assertFprintError('PermissionDenied'): - self.device.DeleteEnrolledFinger('(s)', 'left-little-finger') - - def test_delete_enrolled_fingers_from_other_client(self): - with self.assertFprintError('AlreadyInUse'): - self.call_device_method_from_other_client('DeleteEnrolledFingers', ['testuser']) - - def test_delete_enrolled_fingers2_from_other_client(self): - with self.assertFprintError('AlreadyInUse'): - self.call_device_method_from_other_client('DeleteEnrolledFingers2') - - def test_delete_enrolled_finger_from_other_client(self): - with self.assertFprintError('AlreadyInUse'): - self.call_device_method_from_other_client('DeleteEnrolledFinger', ['left-index-finger']) - - def test_release_from_other_client(self): - with self.assertFprintError('AlreadyInUse'): - self.call_device_method_from_other_client('Release') - - def test_enroll_start_from_other_client(self): - with self.assertFprintError('AlreadyInUse'): - self.call_device_method_from_other_client('EnrollStart', ['left-index-finger']) - - def test_verify_start_from_other_client(self): - with self.assertFprintError('AlreadyInUse'): - self.call_device_method_from_other_client('VerifyStart', ['any']) - - def test_verify_start_finger_from_other_client(self): - with self.assertFprintError('AlreadyInUse'): - self.call_device_method_from_other_client('VerifyStart', ['left-thumb']) - - def test_enroll_finger_status(self): - self.assertFalse(self.finger_present) - self.assertFalse(self.finger_needed) - self.device.EnrollStart('(s)', 'right-middle-finger') - - self.assertEqual(self._changed_properties, []) - - while not self.finger_needed: - ctx.iteration(False) - - self.assertIn({'finger-needed': True}, self._changed_properties) - - self.assertTrue(self.finger_needed) - self.assertFalse(self.finger_present) - - self._changed_properties = [] - self.send_finger_report(True) - self.assertEqual([{'finger-present': True}], self._changed_properties) - self.assertTrue(self.finger_needed) - self.assertTrue(self.finger_present) - - self._changed_properties = [] - self.send_finger_report(False) - self.assertFalse(self.finger_present) - self.assertTrue(self.finger_needed) - self.assertEqual([{'finger-present': False}], self._changed_properties) - - self._changed_properties = [] - self.device.EnrollStop() - - while self.finger_needed: - ctx.iteration(False) - - self.assertFalse(self.finger_present) - self.assertFalse(self.finger_needed) - self.assertEqual([{'finger-needed': False}], self._changed_properties) - - def test_verify_finger_status(self): - self.assertFalse(self.finger_present) - self.assertFalse(self.finger_needed) - self.assertEqual(self._changed_properties, []) - - self.enroll_image('whorl') - - self.assertIn({'finger-needed': True}, self._changed_properties) - self.assertIn({'finger-needed': False}, self._changed_properties) - - self.assertFalse(self.finger_present) - self.assertFalse(self.finger_needed) - - self._changed_properties = [] - self.device.VerifyStart('(s)', 'any') - self.assertEqual(self._changed_properties, []) - - while not self.finger_needed: - ctx.iteration(False) - - self.assertIn({'finger-needed': True}, self._changed_properties) - - self.assertTrue(self.finger_needed) - self.assertFalse(self.finger_present) - - self._changed_properties = [] - self.send_finger_report(True) - self.assertEqual([{'finger-present': True}], self._changed_properties) - self.assertTrue(self.finger_needed) - self.assertTrue(self.finger_present) - - self._changed_properties = [] - self.send_finger_report(False) - self.assertFalse(self.finger_present) - self.assertTrue(self.finger_needed) - self.assertEqual([{'finger-present': False}], self._changed_properties) - - self._changed_properties = [] - self.device.VerifyStop() - - while self.finger_needed: - ctx.iteration(False) - - self.assertFalse(self.finger_present) - self.assertFalse(self.finger_needed) - self.assertEqual([{'finger-needed': False}], self._changed_properties) - - def test_concourrent_enroll_start(self): - self.call_device_method_async('EnrollStart', '(s)', ['left-little-finger']) - self.call_device_method_async('EnrollStart', '(s)', ['left-thumb']) - - with self.assertFprintError('AlreadyInUse'): - self.wait_for_device_reply(expected_replies=2) - - self.assertIn(GLib.Variant('()', ()), self.get_all_async_replies()) - - def test_concourrent_verify_start(self): - self.enroll_image('whorl', finger='left-thumb') - self.call_device_method_async('VerifyStart', '(s)', ['any']) - self.call_device_method_async('VerifyStart', '(s)', ['left-thumb']) - - with self.assertFprintError('AlreadyInUse'): - self.wait_for_device_reply(expected_replies=2) - - self.assertIn(GLib.Variant('()', ()), self.get_all_async_replies()) - - def test_concourrent_list_enrolled_fingers(self): - self.enroll_image('whorl') - self.call_device_method_async('ListEnrolledFingers', '(s)', ['testuser']) - self.call_device_method_async('ListEnrolledFingers', '(s)', ['testuser']) - - # No failure is expected here since it's all sync - self.wait_for_device_reply(expected_replies=2) - self.assertEqual([(['right-index-finger'],), (['right-index-finger'],)], - [ f.unpack() for f in self.get_all_async_replies() ]) - - def test_concourrent_delete_enrolled_fingers(self): - self.enroll_image('whorl') - self.call_device_method_async('DeleteEnrolledFingers', '(s)', ['testuser']) - self.call_device_method_async('DeleteEnrolledFingers', '(s)', ['testuser']) - - accepted_exceptions = ['NoEnrolledPrints'] - if self.device_driver == 'virtual_device_storage': - accepted_exceptions.append('AlreadyInUse') - - self.wait_for_device_reply_relaxed(expected_replies=2, - accepted_exceptions=accepted_exceptions) - - self.assertIn(GLib.Variant('()', ()), self.get_all_async_replies()) - - def test_concourrent_delete_enrolled_fingers_unclaimed(self): - self.enroll_image('whorl') - self.device.Release() - self.call_device_method_async('DeleteEnrolledFingers', '(s)', ['testuser']) - self.call_device_method_async('DeleteEnrolledFingers', '(s)', ['testuser']) - - accepted_exceptions = ['NoEnrolledPrints'] - if self.device_driver == 'virtual_device_storage': - accepted_exceptions.append('AlreadyInUse') - - self.wait_for_device_reply_relaxed(expected_replies=2, - accepted_exceptions=accepted_exceptions) - - self.assertIn(GLib.Variant('()', ()), self.get_all_async_replies()) - - def test_concourrent_delete_enrolled_fingers2(self): - self.enroll_image('whorl') - self.call_device_method_async('DeleteEnrolledFingers2', '()', []) - self.call_device_method_async('DeleteEnrolledFingers2', '()', []) - - accepted_exceptions = ['NoEnrolledPrints'] - if self.device_driver == 'virtual_device_storage': - accepted_exceptions.append('AlreadyInUse') - - self.wait_for_device_reply_relaxed(expected_replies=2, - accepted_exceptions=accepted_exceptions) - - self.assertIn(GLib.Variant('()', ()), self.get_all_async_replies()) - - def test_concourrent_delete_enrolled_finger(self): - self.enroll_image('whorl', finger='left-thumb') - self.enroll_image('tented_arch', finger='right-thumb') - self.call_device_method_async('DeleteEnrolledFinger', '(s)', ['left-thumb']) - self.call_device_method_async('DeleteEnrolledFinger', '(s)', ['right-thumb']) - - accepted_exceptions = [] - if self.device_driver == 'virtual_device_storage': - accepted_exceptions.append('AlreadyInUse') - - self.wait_for_device_reply_relaxed(expected_replies=2, - accepted_exceptions=accepted_exceptions) - - if self.device_driver == 'virtual_device_storage': - self.assertIn(GLib.Variant('()', ()), self.get_all_async_replies()) - else: - self.assertEqual([GLib.Variant('()', ()), GLib.Variant('()', ())], - self.get_all_async_replies()) - - def test_concourrent_release(self): - self.call_device_method_async('Release', '()', []) - self.call_device_method_async('Release', '()', []) - - with self.assertFprintError(['AlreadyInUse', 'ClaimDevice']): - self.wait_for_device_reply(expected_replies=2) - - self.assertIn(GLib.Variant('()', ()), self.get_all_async_replies()) - - def test_already_claimed_same_user_delete_enrolled_fingers(self): - self.enroll_image('whorl') - self.device.DeleteEnrolledFingers('(s)', 'testuser') - - def test_already_claimed_same_user_delete_enrolled_fingers_no_prints(self): - with self.assertFprintError('NoEnrolledPrints'): - self.device.DeleteEnrolledFingers('(s)', 'testuser') - - def test_already_claimed_other_user_delete_enrolled_fingers(self): - self.device.Release() - self.enroll_image('whorl', claim_user='nottestuser') - self.device.Claim('(s)', 'testuser') - self.device.DeleteEnrolledFingers('(s)', 'nottestuser') - - def test_already_claimed_other_user_delete_enrolled_fingers_no_prints(self): - with self.assertFprintError('NoEnrolledPrints'): - self.device.DeleteEnrolledFingers('(s)', 'nottestuser') - - -class FPrintdVirtualDeviceEnrollTests(FPrintdVirtualDeviceBaseTest): - - def setUp(self): - super().setUp() - self._abort = False - self.device.Claim('(s)', 'testuser') - self.device.EnrollStart('(s)', 'left-middle-finger') - self.stop_on_teardown = True - - def tearDown(self): - if self.stop_on_teardown: - self.device.EnrollStop() - self.device.Release() - super().tearDown() - - def assertEnrollRetry(self, device_error, expected_error): - self.send_retry(retry_error=device_error) - self.wait_for_result(expected=expected_error) - - def assertEnrollError(self, device_error, expected_error): - self.send_error(error=device_error) - self.wait_for_result(expected=expected_error) - - def test_enroll_retry_general(self): - self.assertEnrollRetry(FPrint.DeviceRetry.GENERAL, 'enroll-retry-scan') - - def test_enroll_retry_too_short(self): - self.assertEnrollRetry(FPrint.DeviceRetry.TOO_SHORT, 'enroll-swipe-too-short') - - def test_enroll_retry_remove_finger(self): - self.assertEnrollRetry(FPrint.DeviceRetry.REMOVE_FINGER, 'enroll-remove-and-retry') - - def test_enroll_retry_center_finger(self): - self.assertEnrollRetry(FPrint.DeviceRetry.CENTER_FINGER, 'enroll-finger-not-centered') - - def test_enroll_error_general(self): - self.assertEnrollError(FPrint.DeviceError.GENERAL, 'enroll-unknown-error') - - def test_enroll_error_not_supported(self): - self.assertEnrollError(FPrint.DeviceError.NOT_SUPPORTED, 'enroll-unknown-error') - - def test_enroll_error_not_open(self): - self.assertEnrollError(FPrint.DeviceError.NOT_OPEN, 'enroll-unknown-error') - - def test_enroll_error_already_open(self): - self.assertEnrollError(FPrint.DeviceError.ALREADY_OPEN, 'enroll-unknown-error') - - def test_enroll_error_busy(self): - self.assertEnrollError(FPrint.DeviceError.BUSY, 'enroll-unknown-error') - - def test_enroll_error_proto(self): - self.assertEnrollError(FPrint.DeviceError.PROTO, 'enroll-disconnected') - - def test_enroll_error_data_invalid(self): - self.assertEnrollError(FPrint.DeviceError.DATA_INVALID, 'enroll-unknown-error') - - def test_enroll_error_data_not_found(self): - if self.has_identification: - self.assertEnrollError( - FPrint.DeviceError.DATA_NOT_FOUND, 'enroll-stage-passed') - self.assertEnrollError(FPrint.DeviceError.DATA_NOT_FOUND, 'enroll-unknown-error') - - def test_enroll_error_data_full(self): - self.assertEnrollError(FPrint.DeviceError.DATA_FULL, 'enroll-data-full') - - def test_enroll_already_enrolled_finger(self): - self.enroll_image('whorl', start=False) - - # We can enroll a new image deleting the first - self.device.EnrollStart('(s)', 'left-middle-finger') - self.enroll_image('arch', start=False) - self.stop_on_teardown = False - - # If we verify, 'arch' will match, 'whorl' will not match - self.device.VerifyStart('(s)', 'any') - self.send_image('whorl') - self.assertVerifyNoMatch() - self.device.VerifyStop() - - self.device.VerifyStart('(s)', 'any') - self.send_image('arch') - self.assertVerifyMatch() - self.device.VerifyStop() - - def test_enroll_duplicate_image(self): - self.enroll_image('whorl', finger='left-thumb', start=False) - self.enroll_image('whorl', finger='right-thumb', stop=False, - expected_result='enroll-duplicate' if self.has_identification - else 'enroll-completed') - - def test_enroll_start_during_enroll(self): - with self.assertFprintError('AlreadyInUse'): - self.device.EnrollStart('(s)', 'left-thumb') - - def test_verify_start_during_enroll(self): - self.enroll_image('whorl', start=False) - self.device.EnrollStart('(s)', 'right-thumb') - with self.assertFprintError('AlreadyInUse'): - self.device.VerifyStart('(s)', 'any') - - def test_verify_stop_during_enroll(self): - with self.assertFprintError('AlreadyInUse'): - self.device.VerifyStop() - - def test_enroll_stop_from_other_client(self): - with self.assertFprintError('AlreadyInUse'): - self.call_device_method_from_other_client('EnrollStop') - - def test_delete_fingers_during_enroll(self): - with self.assertFprintError('AlreadyInUse'): - self.device.DeleteEnrolledFingers('(s)', '') - - def test_delete_fingers2_during_enroll(self): - with self.assertFprintError('AlreadyInUse'): - self.device.DeleteEnrolledFingers2() - - def test_delete_finger_during_enroll(self): - with self.assertFprintError('AlreadyInUse'): - self.device.DeleteEnrolledFinger('(s)', 'left-thumb') - - def test_enroll_concourrent_stop(self): - self.stop_on_teardown = False - self.call_device_method_async('EnrollStop', '()', []) - self.call_device_method_async('EnrollStop', '()', []) - - with self.assertFprintError(['AlreadyInUse', 'NoActionInProgress']): - self.wait_for_device_reply(method='EnrollStop', expected_replies=2) - - self.assertIn(GLib.Variant('()', ()), self.get_all_async_replies()) - - -class FPrintdVirtualDeviceNoStorageEnrollTests(FPrintdVirtualNoStorageDeviceBaseTest, - FPrintdVirtualDeviceEnrollTests): - # Repeat the tests for the Virtual device (with no storage) - pass - - -class FPrintdVirtualDeviceStorageClaimedTest(FPrintdVirtualStorageDeviceBaseTest, - FPrintdVirtualDeviceClaimedTest): - # Repeat the tests for the Virtual storage device - - def test_release_waits_for_deletion(self): - self.enroll_print('new-print') - self.send_sleep(get_timeout('daemon_stop') * 1000 * 0.5) - self.call_device_method_async('DeleteEnrolledFingers2', '()', []) - self.wait_for_result(max_wait=100) - self.call_device_method_async('Release', '()', []) - with self.assertFprintError('Internal'): - self.wait_for_device_reply(method='Release') - - self.assertFalse(self.get_async_replies( - method='DeleteEnrolledFingers2')) - - def test_delete_enrolled_fingers_device_error(self): - self.enroll_print('new-print') - self.send_sleep(10) - self.send_error(FPrint.DeviceError.BUSY) - - with self.assertFprintError('PrintsNotDeletedFromDevice'): - self.device.DeleteEnrolledFingers('(s)', 'testuser') - - def test_delete_enrolled_fingers2_device_error(self): - self.enroll_print('new-print') - self.send_sleep(10) - self.send_error(FPrint.DeviceError.BUSY) - - with self.assertFprintError('PrintsNotDeletedFromDevice'): - self.device.DeleteEnrolledFingers2() - - def test_delete_enrolled_finger_device_error(self): - self.enroll_print('new-print', finger='left-thumb') - self.send_sleep(10) - self.send_error(FPrint.DeviceError.BUSY) - - with self.assertFprintError('PrintsNotDeletedFromDevice'): - self.device.DeleteEnrolledFinger('(s)', 'left-thumb') - - def test_delete_enrolled_fingers_device_removed(self): - self.enroll_print('deleted-print') - self.send_command('REMOVE', 'deleted-print') - - with self.assertFprintError('PrintsNotDeletedFromDevice'): - self.device.DeleteEnrolledFingers('(s)', 'testuser') - - def test_delete_enrolled_fingers2_device_removed(self): - self.enroll_print('deleted-print') - self.send_command('REMOVE', 'deleted-print') - - with self.assertFprintError('PrintsNotDeletedFromDevice'): - self.device.DeleteEnrolledFingers2() - - def test_delete_enrolled_finger_device_removed(self): - self.enroll_print('deleted-print', finger='left-thumb') - self.send_command('REMOVE', 'deleted-print') - - with self.assertFprintError('PrintsNotDeletedFromDevice'): - self.device.DeleteEnrolledFinger('(s)', 'left-thumb') - - def test_delete_enrolled_fingers_storage_error_has_higher_priority(self): - self.enroll_print('deleted-print', finger='left-thumb') - self.send_sleep(get_timeout('device_sleep')) - self.send_error(FPrint.DeviceError.BUSY) - - self.call_device_method_async('DeleteEnrolledFingers', '(s)', ['testuser']) - self.wait_for_result(max_wait=get_timeout('device_sleep') / 2) - self.assertFalse(self.get_all_async_replies()) - - self.set_print_not_writable('testuser', FPrint.Finger.LEFT_THUMB) - - with self.assertFprintError('PrintsNotDeleted'): - self.wait_for_device_reply() - - def test_delete_enrolled_fingers2_storage_error_has_higher_priority(self): - self.enroll_print('deleted-print', finger='left-thumb') - self.send_sleep(get_timeout('device_sleep')) - self.send_error(FPrint.DeviceError.BUSY) - - self.call_device_method_async('DeleteEnrolledFingers2', '()', []) - self.wait_for_result(max_wait=get_timeout('device_sleep') / 2) - self.assertFalse(self.get_all_async_replies()) - - self.set_print_not_writable('testuser', FPrint.Finger.LEFT_THUMB) - - with self.assertFprintError('PrintsNotDeleted'): - self.wait_for_device_reply() - - def test_delete_enrolled_finger_storage_error_has_higher_priority(self): - self.enroll_print('deleted-print', finger='left-thumb') - self.send_sleep(get_timeout('device_sleep')) - self.send_error(FPrint.DeviceError.BUSY) - - self.call_device_method_async('DeleteEnrolledFinger', '(s)', ['left-thumb']) - self.wait_for_result(max_wait=get_timeout('device_sleep') / 2) - self.assertFalse(self.get_all_async_replies()) - - self.set_print_not_writable('testuser', FPrint.Finger.LEFT_THUMB) - - with self.assertFprintError('PrintsNotDeleted'): - self.wait_for_device_reply() - - def test_release_error(self): - self.send_error(FPrint.DeviceError.PROTO) - - with self.assertFprintError('Internal'): - self.device.Release() - - def test_release_fails_while_closing(self): - self.send_sleep(300) - self.call_device_method_async('Release', '()', []) - self.wait_for_result(max_wait=150) - self.assertFalse(self.get_all_async_replies()) - - with self.assertFprintError('AlreadyInUse'): - self.device.Release() - - self.wait_for_device_reply() - self.assertIn(GLib.Variant('()', ()), self.get_all_async_replies()) - -class FPrintdVirtualDeviceVerificationTests(FPrintdVirtualDeviceBaseTest): - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.enroll_finger = 'left-middle-finger' - cls.verify_finger = cls.enroll_finger - cls.stop_on_teardown = True - cls.releases_on_teardown = True - - def setUp(self): - super().setUp() - self.device.Claim('(s)', 'testuser') - self.enroll_image('whorl', finger=self.enroll_finger) - self.device.VerifyStart('(s)', self.verify_finger) - - def tearDown(self): - if self.stop_on_teardown: - self.device.VerifyStop() - if self.releases_on_teardown: - self.device.Release() - super().tearDown() - - def assertVerifyRetry(self, device_error, expected_error): - self.send_retry(retry_error=device_error) - self.wait_for_result() - self.assertFalse(self._verify_stopped) - self.assertEqual(self._last_result, expected_error) - - def assertVerifyError(self, device_error, expected_error): - self.send_error(error=device_error) - self.wait_for_result() - self.assertTrue(self._verify_stopped) - self.assertEqual(self._last_result, expected_error) - - def test_verify_retry_general(self): - self.assertVerifyRetry(FPrint.DeviceRetry.GENERAL, 'verify-retry-scan') - - def test_verify_retry_general_restarted(self): - self.assertVerifyRetry(FPrint.DeviceRetry.GENERAL, 'verify-retry-scan') - # Give fprintd time to re-start the request. We can't force the other - # case (cancellation before restart happened), but we can force this one. - time.sleep(1) - - def test_verify_retry_too_short(self): - self.assertVerifyRetry(FPrint.DeviceRetry.TOO_SHORT, 'verify-swipe-too-short') - - def test_verify_retry_remove_finger(self): - self.assertVerifyRetry(FPrint.DeviceRetry.REMOVE_FINGER, 'verify-remove-and-retry') - - def test_verify_retry_center_finger(self): - self.assertVerifyRetry(FPrint.DeviceRetry.CENTER_FINGER, 'verify-finger-not-centered') - - def test_verify_error_general(self): - self.assertVerifyError(FPrint.DeviceError.GENERAL, 'verify-unknown-error') - - def test_verify_error_not_supported(self): - self.assertVerifyError(FPrint.DeviceError.NOT_SUPPORTED, 'verify-unknown-error') - - def test_verify_error_not_open(self): - self.assertVerifyError(FPrint.DeviceError.NOT_OPEN, 'verify-unknown-error') - - def test_verify_error_already_open(self): - self.assertVerifyError(FPrint.DeviceError.ALREADY_OPEN, 'verify-unknown-error') - - def test_verify_error_busy(self): - self.assertVerifyError(FPrint.DeviceError.BUSY, 'verify-unknown-error') - - def test_verify_error_proto(self): - self.assertVerifyError(FPrint.DeviceError.PROTO, 'verify-disconnected') - - def test_verify_error_data_invalid(self): - self.assertVerifyError(FPrint.DeviceError.DATA_INVALID, 'verify-unknown-error') - - def test_verify_error_data_not_found(self): - self.assertVerifyError(FPrint.DeviceError.DATA_NOT_FOUND, 'verify-no-match') - - def test_verify_error_data_full(self): - self.assertVerifyError(FPrint.DeviceError.DATA_FULL, 'verify-unknown-error') - - def test_multiple_verify(self): - self.send_image('tented_arch') - self.assertVerifyNoMatch() - self.device.VerifyStop() - - self.device.VerifyStart('(s)', self.verify_finger) - self.send_image('whorl') - self.assertVerifyMatch() - - def start_verify_with_delayed_stop(self, image): - with Connection(self.sockaddr) as con: - self.send_finger_automatic(False, con=con) - self.send_finger_report(True, con=con) - self.send_image(image, con=con) - - def test_multiple_verify_cancelled(self): - self.start_verify_with_delayed_stop('tented_arch') - self.assertVerifyNoMatch() - self.device.VerifyStop() - - self.device.VerifyStart('(s)', self.verify_finger) - self.send_finger_report(False) - self.send_image('whorl') - self.assertVerifyMatch() - - def test_verify_start_during_verify(self): - with self.assertFprintError('AlreadyInUse'): - self.device.VerifyStart('(s)', self.verify_finger) - - def test_enroll_start_during_verify(self): - with self.assertFprintError('AlreadyInUse'): - self.device.EnrollStart('(s)', 'right-thumb') - - def test_enroll_stop_during_verify(self): - with self.assertFprintError('AlreadyInUse'): - self.device.EnrollStop() - - def test_verify_stop_from_other_client(self): - with self.assertFprintError('AlreadyInUse'): - self.call_device_method_from_other_client('VerifyStop') - - def test_delete_fingers_during_verify(self): - with self.assertFprintError('AlreadyInUse'): - self.device.DeleteEnrolledFingers('(s)', '') - - def test_delete_fingers2_during_verify(self): - with self.assertFprintError('AlreadyInUse'): - self.device.DeleteEnrolledFingers2() - - def test_delete_finger_during_verify(self): - with self.assertFprintError('AlreadyInUse'): - self.device.DeleteEnrolledFinger('(s)', 'left-thumb') - - def test_verify_concourrent_stop(self): - self.stop_on_teardown = False - self.call_device_method_async('VerifyStop', '()', []) - self.call_device_method_async('VerifyStop', '()', []) - - with self.assertFprintError(['AlreadyInUse', 'NoActionInProgress']): - self.wait_for_device_reply(method='VerifyStop', expected_replies=2) - - self.assertIn(GLib.Variant('()', ()), self.get_all_async_replies()) - - def test_verify_error_ignored_after_report(self): - if self.device_driver != 'virtual_image': - self.skipTest('Relies on virtual_image driver specifics') - - with Connection(self.sockaddr) as con: - self.send_finger_automatic(False, con=con) - self.send_finger_report(True, con=con) - self.send_image('whorl', con=con) - - self.assertVerifyMatch() - self.assertTrue(self.finger_present) - - self.send_error(con=con) - self.wait_for_result(max_wait=200) - self.assertIsNone(self._last_result) - self.assertFalse(self.finger_present) - - def test_verify_stop_waits_for_completion(self): - self.stop_on_teardown = False - - self.start_verify_with_delayed_stop('tented_arch') - self.assertVerifyNoMatch() - - self.call_device_method_async('VerifyStop', '()', []) - - def restart_verify(abort=False): - self.call_device_method_async('VerifyStart', '(s)', [self.verify_finger]) - with self.assertFprintError('AlreadyInUse'): - self.wait_for_device_reply(method='VerifyStart') - - self.assertFalse(self.get_async_replies(method='VerifyStop')) - self._abort = abort - - restart_verify() - GLib.timeout_add(100, restart_verify) - GLib.timeout_add(300, restart_verify, True) - self.wait_for_result() - - def test_verify_stop_waits_for_completion_waiting_timeout(self): - self.test_verify_stop_waits_for_completion() - self.wait_for_device_reply(method='VerifyStop') - self.assertTrue(self.get_async_replies(method='VerifyStop')) - - def test_verify_stop_waits_for_completion_is_stopped_by_release(self): - # During the release here we're testing the case in which - # while we're waiting for VerifyStop to return, Release stops the - # verification, making the invocation to return - self.releases_on_teardown = False - self.test_verify_stop_waits_for_completion() - self.assertFalse(self.get_async_replies(method='VerifyStop')) - self.call_device_method_async('Release', '()', []) - self.wait_for_device_reply(method='Release') - self.assertTrue(self.get_async_replies(method='VerifyStop')) - - -class FPrintdVirtualDeviceStorageVerificationUtils(object): - def start_verify_with_delayed_stop(self, image, match=None): - self.send_sleep(50) - self.send_image(image) - self.send_sleep(get_timeout('test') * 1000) - - def test_verify_error_ignored_after_report(self): - self.send_sleep(50) - self.send_image('whorl') - self.send_sleep(0) - self.send_error(FPrint.DeviceError.BUSY) - - self.assertVerifyMatch() - - self.wait_for_result(max_wait=200) - self.assertIsNone(self._last_result) - self.assertFalse(self.finger_present) - -class FPrintdVirtualDeviceStorageVerificationTests(FPrintdVirtualStorageDeviceBaseTest, - FPrintdVirtualDeviceStorageVerificationUtils, - FPrintdVirtualDeviceVerificationTests): - # Repeat the tests for the Virtual storage device, with specific overrides - pass - -class FPrintdVirtualDeviceNoStorageVerificationTests(FPrintdVirtualNoStorageDeviceBaseTest, - FPrintdVirtualDeviceStorageVerificationTests): - # Repeat the tests for the Virtual device (with no storage) - pass - -class FPrintdVirtualDeviceIdentificationTests(FPrintdVirtualDeviceVerificationTests): - '''This class will just repeat the tests of FPrintdVirtualDeviceVerificationTests - but with 'any' finger parameter (leading to an identification, when possible - under the hood). - ''' - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.verify_finger = 'any' - - -class FPrintdVirtualDeviceStorageIdentificationTests(FPrintdVirtualStorageDeviceBaseTest, - FPrintdVirtualDeviceStorageVerificationUtils, - FPrintdVirtualDeviceIdentificationTests): - # Repeat the tests for the Virtual storage device - pass - -class FPrintdVirtualDeviceNoStorageIdentificationTests(FPrintdVirtualNoStorageDeviceBaseTest, - FPrintdVirtualDeviceStorageIdentificationTests): - # Repeat the tests for the Virtual device (with no storage) - pass - - -class FPrindConcurrentPolkitRequestsTest(FPrintdVirtualStorageDeviceBaseTest): - - def wait_for_hanging_clients(self): - while not self._polkitd_obj.HaveHangingCalls(): - pass - self.assertTrue(self._polkitd_obj.HaveHangingCalls()) - - def start_hanging_gdbus_claim(self, user='testuser'): - gdbus = self.gdbus_device_method_call_process('Claim', [user]) - self.assertIsNone(gdbus.poll()) - self.wait_for_hanging_clients() - self.addCleanup(gdbus.kill) - return gdbus - - def test_hanging_claim_does_not_block_new_claim_external_client(self): - self._polkitd_obj.SetAllowed([ - FprintDevicePermission.set_username, - FprintDevicePermission.enroll ]) - self._polkitd_obj.SimulateHang(True) - self._polkitd_obj.SetDelay(0.5) - - gdbus = self.start_hanging_gdbus_claim() - - self._polkitd_obj.SimulateHang(False) - self.device.Claim('(s)', self.get_current_user()) - - self.assertIsNone(gdbus.poll()) - self._polkitd_obj.ReleaseHangingCalls() - - gdbus.wait() - with self.assertFprintError('AlreadyInUse'): - raise GLib.GError(gdbus.stdout.read()) - - self.device.Release() - - def test_hanging_claim_does_not_block_new_claim(self): - self._polkitd_obj.SetAllowed([ - FprintDevicePermission.set_username, - FprintDevicePermission.enroll ]) - self._polkitd_obj.SimulateHang(True) - self._polkitd_obj.SetDelay(0.5) - - self.call_device_method_async('Claim', '(s)', ['']) - self.wait_for_hanging_clients() - - self._polkitd_obj.SimulateHang(False) - self.device.Claim('(s)', self.get_current_user()) - - self._polkitd_obj.ReleaseHangingCalls() - - with self.assertFprintError('AlreadyInUse'): - self.wait_for_device_reply() - - self.device.Release() - - def test_hanging_claim_enroll_does_not_block_new_claim(self): - self._polkitd_obj.SetAllowed([ - FprintDevicePermission.set_username, - FprintDevicePermission.enroll ]) - self._polkitd_obj.SimulateHangActions([ - FprintDevicePermission.enroll]) - self._polkitd_obj.SetDelay(0.5) - - gdbus = self.start_hanging_gdbus_claim() - - self._polkitd_obj.SimulateHangActions(['']) - self.device.Claim('(s)', self.get_current_user()) - - self.assertIsNone(gdbus.poll()) - self._polkitd_obj.ReleaseHangingCalls() - - gdbus.wait() - with self.assertFprintError('AlreadyInUse'): - raise GLib.GError(gdbus.stdout.read()) - - self.device.Release() - - def test_hanging_claim_does_not_block_new_release(self): - self._polkitd_obj.SetAllowed([FprintDevicePermission.set_username]) - self._polkitd_obj.SimulateHang(True) - - gdbus = self.gdbus_device_method_call_process('Claim', ['testuser']) - self.addCleanup(gdbus.kill) - - self.wait_for_hanging_clients() - with self.assertFprintError('ClaimDevice'): - self.device.Release() - - self.assertIsNone(gdbus.poll()) - - def test_hanging_claim_does_not_block_list(self): - self._polkitd_obj.SetAllowed([ - FprintDevicePermission.set_username, - FprintDevicePermission.enroll, - FprintDevicePermission.verify]) - - self.device.Claim('(s)', '') - self.enroll_image('whorl', finger='left-thumb') - self.device.Release() - - self._polkitd_obj.SimulateHangActions([ - FprintDevicePermission.set_username]) - - gdbus = self.start_hanging_gdbus_claim() - - self.assertEqual(self.device.ListEnrolledFingers('(s)', - self.get_current_user()), ['left-thumb']) - - self.assertIsNone(gdbus.poll()) - - def test_hanging_claim_can_proceed_when_released(self): - self._polkitd_obj.SetAllowed([ - FprintDevicePermission.set_username, - FprintDevicePermission.verify]) - - self._polkitd_obj.SimulateHangActions([ - FprintDevicePermission.set_username]) - - gdbus = self.start_hanging_gdbus_claim() - - self._polkitd_obj.SimulateHangActions(['']) - self.device.Claim('(s)', 'testuser') - self.device.Release() - - self.assertIsNone(gdbus.poll()) - - self._polkitd_obj.ReleaseHangingCalls() - gdbus.wait() - - self.assertEqual(gdbus.returncode, 0) - - def test_hanging_claim_does_not_block_empty_list(self): - self._polkitd_obj.SetAllowed([ - FprintDevicePermission.set_username, - FprintDevicePermission.enroll, - FprintDevicePermission.verify]) - - self._polkitd_obj.SimulateHangActions([ - FprintDevicePermission.set_username]) - - gdbus = self.start_hanging_gdbus_claim() - - with self.assertFprintError('NoEnrolledPrints'): - self.device.ListEnrolledFingers('(s)', self.get_current_user()) - - self.assertIsNone(gdbus.poll()) - - def test_hanging_claim_does_not_block_verification(self): - self._polkitd_obj.SetAllowed([ - FprintDevicePermission.set_username, - FprintDevicePermission.enroll, - FprintDevicePermission.verify]) - - self.device.Claim('(s)', '') - self.enroll_image('whorl', finger='left-thumb') - self.device.Release() - - self._polkitd_obj.SimulateHangActions([ - FprintDevicePermission.set_username]) - - gdbus = self.start_hanging_gdbus_claim() - - self.device.Claim('(s)', '') - self.device.VerifyStart('(s)', 'any') - self.send_image('whorl') - self.assertVerifyMatch(selected_finger='left-thumb') - self.device.VerifyStop() - self.device.Release() - - self.assertIsNone(gdbus.poll()) - - -class FPrintdUtilsTest(FPrintdVirtualStorageDeviceBaseTest): - - @classmethod - def setUpClass(cls): - super().setUpClass() - utils = { - 'delete': None, - 'enroll': None, - 'list': None, - 'verify': None, - } - - for util in utils: - util_bin = 'fprintd-{}'.format(util) - if 'FPRINT_BUILD_DIR' in os.environ: - print('Testing local build') - build_dir = os.environ['FPRINT_BUILD_DIR'] - path = os.path.join(build_dir, '../utils', util_bin) - elif 'UNDER_JHBUILD' in os.environ: - print('Testing JHBuild version') - jhbuild_prefix = os.environ['JHBUILD_PREFIX'] - path = os.path.join(jhbuild_prefix, 'bin', util_bin) - else: - # Assume it is in path - utils[util] = util_bin - continue - - assert os.path.exists(path), 'failed to find {} in {}'.format(util, path) - utils[util] = path - - cls.utils = utils - cls.utils_proc = {} - - def util_start(self, name, args=[]): - env = os.environ.copy() - env['G_DEBUG'] = 'fatal-criticals' - env['STATE_DIRECTORY'] = self.state_dir - env['RUNTIME_DIRECTORY'] = self.run_dir - - argv = [self.utils[name]] + args - valgrind = os.getenv('VALGRIND') - if valgrind is not None: - argv.insert(0, 'valgrind') - argv.insert(1, '--leak-check=full') - if os.path.exists(valgrind): - argv.insert(2, '--suppressions=%s' % valgrind) - self.valgrind = True - output = OutputChecker() - self.utils_proc[name] = subprocess.Popen(argv, - env=env, - stdout=output.fd, - stderr=subprocess.STDOUT) - output.writer_attached() - self.addCleanup(self.utils_proc[name].wait) - self.addCleanup(self.utils_proc[name].terminate) - self.addCleanup(output.assert_closed) - return self.utils_proc[name], output - - def test_vanished_client_operation_is_cancelled(self): - self.device.Claim('(s)', self.get_current_user()) - self.enroll_image('whorl') - self.device.Release() - - verify, output = self.util_start('verify') - time.sleep(1) - verify.terminate() - self.assertLess(verify.wait(), 128) - time.sleep(1) - - self.device.Claim('(s)', self.get_current_user()) - self.device.Release() - - def test_delete_no_prints(self): - delete, out = self.util_start('delete', [self.get_current_user()]) - out.check_line('No fingerprints to delete on {}'.format( - self.driver_name).encode('utf-8'), get_timeout()) - self.assertEqual(delete.wait(), 0) - - def test_delete_already_claimed(self): - self.device.Claim('(s)', self.get_current_user()) - self.addCleanup(self.try_release) - self.enroll_image('whorl') - - delete, out = self.util_start('delete', [self.get_current_user()]) - out.check_line('{}.Error.AlreadyInUse'.format(FPRINT_NAMESPACE), get_timeout()) - self.assertNotEqual(delete.wait(), 0) - self.assertLess(delete.wait(), 128) - - def test_delete_error_claiming(self): - self.device.Claim('(s)', self.get_current_user()) - self.addCleanup(self.try_release) - self.set_keep_alive(True) - self.device.Release() - - self.send_error(FPrint.DeviceError.PROTO) - delete, out = self.util_start('delete', [self.get_current_user()]) - out.check_line('{}.Error.Internal'.format(FPRINT_NAMESPACE), get_timeout()) - self.assertNotEqual(delete.wait(), 0) - self.assertLess(delete.wait(), 128) - - def test_delete_error(self): - self.device.Claim('(s)', self.get_current_user()) - self.addCleanup(self.try_release) - self.enroll_image('whorl') - self.set_keep_alive(True) - self.device.Release() - - self.send_command('IGNORED_COMMAND') # During claim - self.send_error(FPrint.DeviceError.PROTO) # During delete - - delete, out = self.util_start('delete', [self.get_current_user()]) - out.check_line('Failed to delete fingerprints', get_timeout()) - self.assertNotEqual(delete.wait(), 0) - self.assertLess(delete.wait(), 128) - - def test_delete_release_error(self): - self.device.Claim('(s)', self.get_current_user()) - self.addCleanup(self.try_release) - self.set_keep_alive(True) - self.device.Release() - - self.send_command('IGNORED_COMMAND') # During claim - self.send_error(FPrint.DeviceError.PROTO) # During release - - delete, out = self.util_start('delete', [self.get_current_user()]) - out.check_line('Release failed with error', get_timeout()) - self.assertNotEqual(delete.wait(), 0) - self.assertLess(delete.wait(), 128) - - def test_delete_single_finger(self): - self.device.Claim('(s)', 'testuser') - enrolled, enroll_map = self.enroll_multiple_images() - self.addCleanup(self.try_release) - self.device.Release() - - finger_name = enrolled[0] - delete, out = self.util_start('delete', ['testuser', - '-f', finger_name]) - - out.check_line('Using device {}'.format( - self.device.get_object_path()), get_timeout()) - - out.check_line('Fingerprint {} of user {} deleted on {}'.format( - finger_name, 'testuser', self.driver_name), get_timeout()) - self.assertEqual(delete.wait(), 0) - - remaining = self.device.ListEnrolledFingers('(s)', 'testuser') - self.assertNotIn(finger_name, remaining) - self.assertCountEqual(enrolled[1:], remaining) - - def test_delete_multiple_users_single_finger(self): - self.addCleanup(self.try_release) - enroll_map, enrolled_prints_info = self.enroll_users_images() - delete_args = [] - for user, print_info in enroll_map.items(): - for f in print_info: - delete_args.append(user) - delete_args.append('-f') - delete_args.append(f) - - delete, out = self.util_start('delete', delete_args) - out.check_line('Using device {}'.format( - self.device.get_object_path()), get_timeout()) - - for user, print_info in enroll_map.items(): - for f in print_info: - out.check_line('Fingerprint {} of user {} deleted on {}'.format( - f, user, self.driver_name), get_timeout()) - - self.assertEqual(delete.wait(), 0) - with self.assertFprintError('NoEnrolledPrints'): - self.device.ListEnrolledFingers('(s)', 'testuser') - - def test_enroll(self): - self.device.Claim('(s)', self.get_current_user()) - self.set_keep_alive(True) - self.device.Release() - - # Open (no clear storage as list is supported) - self.send_command('CONT') - - finger_name = self.get_finger_name(FPrint.Finger.LEFT_THUMB) - enroll, out = self.util_start('enroll', [self.get_current_user(), - '-f', finger_name]) - out.check_line('Using device {}'.format( - self.device.get_object_path()), get_timeout()) - - out.check_line('Enrolling {} finger.'.format(finger_name).encode('utf-8'), - get_timeout()) - - self.send_image('print-id') - out.check_line('Enroll result: enroll-stage-passed', get_timeout()) - - self.send_image('print-id') - out.check_line('Enroll result: enroll-stage-passed', get_timeout()) - - self.send_retry(FPrint.DeviceRetry.TOO_SHORT) - out.check_line('Enroll result: enroll-swipe-too-short', get_timeout()) - - self.send_image('print-id') - out.check_line('Enroll result: enroll-stage-passed', get_timeout()) - - self.send_image('print-id') - out.check_line('Enroll result: enroll-stage-passed', get_timeout()) - - self.send_image('print-id') - out.check_line('Enroll result: enroll-stage-passed', get_timeout()) - - self.send_retry(FPrint.DeviceRetry.CENTER_FINGER) - out.check_line('Enroll result: enroll-finger-not-centered', get_timeout()) - - self.send_image('print-id') - out.check_line('Enroll result: enroll-completed', get_timeout()) - - self.assertEqual(enroll.wait(), 0) - - def test_enroll_error(self): - self.device.Claim('(s)', self.get_current_user()) - self.set_keep_alive(True) - self.device.Release() - - # Open (no clear storage as list is supported) - self.send_command('CONT') - - finger_name = self.get_finger_name(FPrint.Finger.LEFT_MIDDLE) - enroll, out = self.util_start('enroll', [self.get_current_user(), - '-f', finger_name]) - out.check_line('Using device {}'.format( - self.device.get_object_path()), get_timeout()) - - out.check_line('Enrolling {} finger.'.format(finger_name).encode('utf-8'), - get_timeout()) - - self.send_image('print-id') - out.check_line('Enroll result: enroll-stage-passed', get_timeout()) - - self.send_image('print-id') - out.check_line('Enroll result: enroll-stage-passed', get_timeout()) - - self.send_retry(FPrint.DeviceRetry.TOO_SHORT) - out.check_line('Enroll result: enroll-swipe-too-short', get_timeout()) - - self.send_error(FPrint.DeviceError.PROTO) - out.check_line('Enroll result: enroll-disconnected', get_timeout()) - - self.assertNotEqual(enroll.wait(), 0) - self.assertLess(enroll.wait(), 128) - - def test_enroll_error_invalid_finger(self): - finger_name = 'eleventh-hand-finger' - enroll, out = self.util_start('enroll', [self.get_current_user(), - '-f', finger_name]) - - out.check_line('Invalid finger name \'{}\''.format(finger_name), get_timeout()) - - self.assertNotEqual(enroll.wait(), 0) - self.assertLess(enroll.wait(), 128) - - def run_verify(self, finger, match, error=None): - self.device.Claim('(s)', 'testuser') - finger_name = self.get_finger_name(finger) - override = {} if finger is FPrint.Finger.UNKNOWN else { - finger_name: 'print-id', - } - enrolled, enroll_map = self.enroll_multiple_images( - images_override=override) - self.set_keep_alive(True) - self.device.Release() - - verify, out = self.util_start( - 'verify', ['-f', finger_name, 'testuser']) - out.check_line('Using device {}'.format( - self.device.get_object_path()), get_timeout()) - out.check_line('Verify started!', get_timeout()) - out.check_line('Verifying: {}'.format(finger_name), get_timeout()) - - if error: - self.send_error(error) - self.assertNotEqual(verify.wait(), 0) - self.assertLess(verify.wait(), 128) - out.check_line('Verify result: verify-disconnected (done)', get_timeout()) - elif match: - verify_finger = enrolled[0] if finger is FPrint.Finger.UNKNOWN else finger_name - self.send_image(enroll_map[verify_finger]) - out.check_line('Verify result: verify-match (done)', get_timeout()) - self.assertEqual(verify.wait(), 0) - else: - if finger is FPrint.Finger.UNKNOWN: - verify_image = 'another-print' - else: - enroll_map.pop(finger_name) - verify_image = list(enroll_map.values())[0] - self.send_image(verify_image) - out.check_line('Verify result: verify-no-match (done)', get_timeout()) - self.assertNotEqual(verify.wait(), 0) - self.assertLess(verify.wait(), 128) - - def test_verify_match(self): - self.run_verify(finger=FPrint.Finger.RIGHT_THUMB, match=True) - - def test_verify_no_match(self): - self.run_verify(finger=FPrint.Finger.LEFT_MIDDLE, match=False) - - def test_verify_error(self): - self.run_verify(finger=FPrint.Finger.RIGHT_THUMB, match=False, - error=FPrint.DeviceError.PROTO) - - def test_verify_any_finger_match(self): - self.run_verify(finger=FPrint.Finger.UNKNOWN, match=True) - - def test_verify_any_finger_no_match(self): - self.run_verify(finger=FPrint.Finger.UNKNOWN, match=False) - - def test_verify_any_finger_error(self): - self.run_verify(finger=FPrint.Finger.UNKNOWN, match=False, - error=FPrint.DeviceError.PROTO) - - -def list_tests(): - import unittest_inspector - return unittest_inspector.list_tests(sys.modules[__name__]) - -if __name__ == '__main__': - if len(sys.argv) == 2 and sys.argv[1] == "list-tests": - for machine, human in list_tests(): - print("%s %s" % (machine, human), end="\n") - sys.exit(0) - - prog = unittest.main(verbosity=2, exit=False) - if prog.result.errors or prog.result.failures: - sys.exit(1) - - # Translate to skip error - if prog.result.testsRun == len(prog.result.skipped): - sys.exit(77) - - sys.exit(0) diff --git a/tests/meson.build b/tests/meson.build deleted file mode 100644 index 495acbc..0000000 --- a/tests/meson.build +++ /dev/null @@ -1,126 +0,0 @@ -# Add a way to discover and run python unit tests separately -# https://github.com/mesonbuild/meson/issues/6851 -python_tests = [ - # List all the python tests, must be in the form: - # { - # 'name': 'test name', - # 'file': 'full test file path, use files('path')[0]', - # Fields below are optional: - # 'workdir': '', - # 'env': [], - # 'depends': [], - # 'suite': [], - # 'extra_args': [], - # 'timeout': 30, - # 'is_parallel': true, - # } -] - -address_sanitizer = get_option('b_sanitize') == 'address' - -tests = [ - 'fprintd', - 'test_fprintd_utils', -] - -foreach t: tests - python_tests += [ - { - 'name': t, - 'file': files(meson.current_source_dir() / t + '.py')[0], - 'env': [ - 'G_DEBUG=fatal-criticals', - 'G_MESSAGES_DEBUG=all', - 'FPRINT_BUILD_DIR=' + meson.build_root() / 'src', - 'TOPSRCDIR=' + meson.source_root(), - ], - 'depends': [ - fprintd, - fprintd_utils, - ], - 'suite': [t == 'fprintd' ? 'daemon' : ''], - } - ] -endforeach - -if get_option('pam') - subdir('pam') -endif - -# Add a way to discover and run python unit tests separately -# https://github.com/mesonbuild/meson/issues/6851 -unittest_inspector = find_program('unittest_inspector.py') - -foreach pt: python_tests - r = run_command(unittest_inspector, pt.get('file')) - unit_tests = r.stdout().strip().split('\n') - base_args = [ pt.get('file') ] + pt.get('extra_args', []) - suite = pt.get('suite', []) - - if r.returncode() == 0 and unit_tests.length() > 0 - suite += pt.get('name') - else - unit_tests = [pt.get('name')] - endif - - foreach ut: unit_tests - ut_suite = suite - ut_args = base_args - if unit_tests.length() > 1 - ut_args += ut - ut_suite += ut.split('.')[0] - endif - test(ut, - python3, - args: ut_args, - suite: ut_suite, - depends: pt.get('depends', []), - workdir: pt.get('workdir', meson.build_root()), - env: pt.get('env', []), - timeout: pt.get('timeout', 30), - is_parallel: pt.get('is_parallel', true), - ) - endforeach -endforeach - -timeout_multiplier = 1 -test_envs = [ - 'G_SLICE=always-malloc', - 'MALLOC_CHECK_=2', -] - -if address_sanitizer - timeout_multiplier = 3 - test_envs += [ - 'ADDRESS_SANITIZER=true', - 'ASAN_OPTIONS=@0@'.format(':'.join([ - 'abort_on_error=true', - 'symbolize=true', - ])), - 'LSAN_OPTIONS=@0@'.format(':'.join([ - 'exitcode=0', - 'strict_string_checks=true', - 'suppressions=@0@'.format( - files(meson.current_source_dir() / 'LSAN-leaks-suppress.txt')[0]), - ])), - ] -endif - -add_test_setup('default_setup', - is_default: true, - env: test_envs, - timeout_multiplier: timeout_multiplier -) - -if not address_sanitizer and find_program('valgrind', required: false).found() - glib_share = glib_dep.get_pkgconfig_variable('prefix') / 'share' / glib_dep.name() - glib_suppressions = glib_share + '/valgrind/glib.supp' - add_test_setup('valgrind', - env: [ - 'G_SLICE=always-malloc', - 'VALGRIND=' + glib_suppressions, - ], - timeout_multiplier: 5 - ) -endif - diff --git a/tests/output_checker.py b/tests/output_checker.py deleted file mode 100644 index 265e323..0000000 --- a/tests/output_checker.py +++ /dev/null @@ -1,199 +0,0 @@ -#! /usr/bin/env python3 -# Copyright © 2020, RedHat Inc. -# -# 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 2.1 of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. If not, see . -# Authors: -# Benjamin Berg - -import os -import sys -import fcntl -import io -import re -import time -import threading -import select -import errno - -class OutputChecker(object): - - def __init__(self, out=sys.stdout): - self._output = out - self._pipe_fd_r, self._pipe_fd_w = os.pipe() - self._partial_buf = b'' - self._lines_sem = threading.Semaphore() - self._lines = [] - self._reader_io = io.StringIO() - - # Just to be sure, shouldn't be a problem even if we didn't set it - fcntl.fcntl(self._pipe_fd_r, fcntl.F_SETFL, - fcntl.fcntl(self._pipe_fd_r, fcntl.F_GETFL) | os.O_CLOEXEC | os.O_NONBLOCK) - fcntl.fcntl(self._pipe_fd_w, fcntl.F_SETFL, - fcntl.fcntl(self._pipe_fd_w, fcntl.F_GETFL) | os.O_CLOEXEC) - - # Start copier thread - self._thread = threading.Thread(target=self._copy, daemon=True) - self._thread.start() - - def _copy(self): - p = select.poll() - p.register(self._pipe_fd_r) - while True: - try: - # Be lazy and wake up occasionally in case _pipe_fd_r became invalid - # The reason to do this is because os.read() will *not* return if the - # FD is forcefully closed. - p.poll(0.1) - - r = os.read(self._pipe_fd_r, 1024) - if not r: - os.close(self._pipe_fd_r) - self._pipe_fd_r = -1 - self._lines_sem.release() - return - except OSError as e: - if e.errno == errno.EWOULDBLOCK: - continue - - # We get a bad file descriptor error when the outside closes the FD - if self._pipe_fd_r >= 0: - os.close(self._pipe_fd_r) - self._pipe_fd_r = -1 - self._lines_sem.release() - return - - l = r.split(b'\n') - l[0] = self._partial_buf + l[0] - self._lines.extend(l[:-1]) - self._partial_buf = l[-1] - - self._lines_sem.release() - - os.write(self._output.fileno(), r) - - def check_line_re(self, needle_re, timeout=0, failmsg=None): - deadline = time.time() + timeout - - if isinstance(needle_re, str): - needle_re = needle_re.encode('ascii') - - r = re.compile(needle_re) - ret = [] - - while True: - try: - l = self._lines.pop(0) - except IndexError: - # EOF, throw error - if self._pipe_fd_r == -1: - if failmsg: - raise AssertionError("No further messages: " % failmsg) - else: - raise AssertionError('No client waiting for needle %s' % (str(needle_re))) - - # Check if should wake up - if not self._lines_sem.acquire(timeout = deadline - time.time()): - if failmsg: - raise AssertionError(failmsg) - else: - raise AssertionError('Timed out waiting for needle %s (timeout: %0.2f)' % (str(needle_re), timeout)) - continue - - ret.append(l) - if r.search(l): - return ret - - def check_line(self, needle, timeout=0, failmsg=None): - if isinstance(needle, str): - needle = needle.encode('ascii') - - needle_re = re.escape(needle) - - return self.check_line_re(needle_re, timeout=timeout, failmsg=failmsg) - - def check_no_line_re(self, needle_re, wait=0, failmsg=None): - deadline = time.time() + wait - - if isinstance(needle_re, str): - needle_re = needle_re.encode('ascii') - - r = re.compile(needle_re) - ret = [] - - while True: - try: - l = self._lines.pop(0) - except IndexError: - # EOF, so everything good - if self._pipe_fd_r == -1: - break - - # Check if should wake up - if not self._lines_sem.acquire(timeout = deadline - time.time()): - # Timed out, so everything is good - break - continue - - ret.append(l) - if r.search(l): - if failmsg: - raise AssertionError(failmsg) - else: - raise AssertionError('Found needle %s but shouldn\'t have been there (timeout: %0.2f)' % (str(needle_re), wait)) - - return ret - - def check_no_line(self, needle, wait=0, failmsg=None): - if isinstance(needle, str): - needle = needle.encode('ascii') - - needle_re = re.escape(needle) - - return self.check_no_line_re(needle_re, wait=wait, failmsg=failmsg) - - def clear(self): - ret = self._lines - self._lines = [] - return ret - - def assert_closed(self, timeout=1): - self._thread.join(timeout) - - if self._thread.is_alive() != False: - raise AssertionError("OutputCheck: Write side has not been closed yet!") - - def force_close(self): - - fd = self._pipe_fd_r - self._pipe_fd_r = -1 - if fd >= 0: - os.close(fd) - - self._thread.join() - - @property - def fd(self): - return self._pipe_fd_w - - def writer_attached(self): - os.close(self._pipe_fd_w) - self._pipe_fd_w = -1 - - def __del__(self): - if self._pipe_fd_r >= 0: - os.close(self._pipe_fd_r) - self._pipe_fd_r = -1 - if self._pipe_fd_w >= 0: - os.close(self._pipe_fd_w) - self._pipe_fd_w = -1 diff --git a/tests/pam/meson.build b/tests/pam/meson.build deleted file mode 100644 index dc0b071..0000000 --- a/tests/pam/meson.build +++ /dev/null @@ -1,59 +0,0 @@ -subdir('services') - -tests = [ - 'test_pam_fprintd', -] - -preloaded_libs = [] -pam_tests_ld_preload = [] - -if address_sanitizer - # ASAN has to be the first in list - preloaded_libs += 'asan' -endif - -preloaded_libs += 'pam_wrapper' - -foreach libname: preloaded_libs - lib = run_command(meson.get_compiler('c'), - '-print-file-name=lib@0@.so'.format(libname) - ).stdout().strip() - - # Support linker script files - if run_command('grep', '-qI', '^INPUT', files(lib)).returncode() == 0 - out = run_command('cat', lib).stdout() - lib = out.split('(')[1].split(')')[0].strip() - endif - - if lib != '' and lib[0] == '/' - message('Found library @0@ as @1@'.format(libname, lib)) - pam_tests_ld_preload += '@0@'.format(files(lib)[0]) - else - tests = [] - warning('No library found for ' + libname + ', skipping PAM tests') - endif -endforeach - -foreach t: tests - python_tests += [ - { - 'name': t, - 'file': files(meson.current_source_dir() / t + '.py')[0], - 'is_parallel': false, - 'env': [ - 'TOPBUILDDIR=' + meson.build_root(), - 'TOPSRCDIR=' + meson.source_root(), - 'LD_PRELOAD=' + ' '.join(pam_tests_ld_preload), - 'PAM_WRAPPER=1', - 'PAM_WRAPPER_DEBUGLEVEL=2', - 'PAM_WRAPPER_SERVICE_DIR=' + meson.current_build_dir() / 'services', - 'G_DEBUG=fatal-warnings', - ], - 'depends': [ - pam_fprintd, - pam_service_file, - ], - 'suite': ['PAM'], - } - ] -endforeach diff --git a/tests/pam/services/fprintd-pam-test.in b/tests/pam/services/fprintd-pam-test.in deleted file mode 100644 index f3fb5c3..0000000 --- a/tests/pam/services/fprintd-pam-test.in +++ /dev/null @@ -1 +0,0 @@ -auth required @FPRINTDPAMPATH@ debug timeout=10 diff --git a/tests/pam/services/meson.build b/tests/pam/services/meson.build deleted file mode 100644 index b9e66e1..0000000 --- a/tests/pam/services/meson.build +++ /dev/null @@ -1,13 +0,0 @@ -# Meson doesn't allow to have configure_file's as targets we depend on... Meh! -pam_service_file = custom_target('pam_test_service_file', - output: 'null', - command: 'true', - depends: pam_fprintd, - depend_files: configure_file( - input: 'fprintd-pam-test.in', - output: 'fprintd-pam-test', - configuration: configuration_data({ - 'FPRINTDPAMPATH': pam_fprintd.full_path(), - }), - ), -) diff --git a/tests/pam/test_pam_fprintd.py b/tests/pam/test_pam_fprintd.py deleted file mode 100644 index 99f8259..0000000 --- a/tests/pam/test_pam_fprintd.py +++ /dev/null @@ -1,351 +0,0 @@ -#!/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 dbusmock -import glob -import os -import shutil -import time -import pypamtest - -PAM_SUCCESS = 0 -PAM_AUTH_ERR = 7 -PAM_AUTHINFO_UNAVAIL = 9 -PAM_USER_UNKNOWN = 10 -PAM_MAXTRIES = 11 - -class TestPamFprintd(dbusmock.DBusTestCase): - '''Test pam_fprintd''' - - @classmethod - def start_monitor(klass): - '''Start dbus-monitor''' - - workdir = os.environ['TOPBUILDDIR'] + '/tests/pam/' - klass.monitor_log = open(os.path.join(workdir, 'dbus-monitor.log'), 'wb', buffering=0) - klass.monitor = subprocess.Popen(['dbus-monitor', '--monitor', '--system'], - stdout=klass.monitor_log, - stderr=subprocess.STDOUT) - - @classmethod - def stop_monitor(klass): - '''Stop dbus-monitor''' - - assert klass.monitor - klass.monitor.terminate() - klass.monitor.wait() - - klass.monitor_log.flush() - klass.monitor_log.close() - - @classmethod - def setUpClass(klass): - klass.start_system_bus() - klass.start_monitor() - 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) - - @classmethod - def tearDownClass(klass): - klass.stop_monitor() - - # Remove pam wrapper files, as they may break other tests - [shutil.rmtree(i) for i in glob.glob('/tmp/pam.[0-9A-z]')] - - def setUp(self): - (self.p_mock, self.obj_fprintd_manager) = self.spawn_server_template( - self.template_name, {}) - 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_no_device(self): - tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTHINFO_UNAVAIL) - res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ]) - - def test_pam_fprintd_identify_error(self): - self.setup_device() - script = [ - ( 'verify-unknown-error', True, 2 ) - ] - self.device_mock.SetVerifyScript(script) - - tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTHINFO_UNAVAIL) - 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_identify_error2(self): - self.setup_device() - script = [ - ( 'verify-disconnected', True, 2 ) - ] - self.device_mock.SetVerifyScript(script) - - tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTHINFO_UNAVAIL) - 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_identify_error3(self): - self.setup_device() - script = [ - ( 'verify-INVALID', True, 2 ) - ] - 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), 1) - self.assertRegex(res.errors[0], r'An unknown error occurred') - - 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) - - # Check that we can stop verification and release the device. i.e. - # this has not been done by PAM already (the real fprintd would notice - # the disconnect, the mock service does not). - self.device_mock.VerifyStop() - self.device_mock.Release() - - def test_pam_fprintd_no_fingers(self): - self.setup_device() - self.device_mock.SetEnrolledFingers('toto', dbus.Array(set([]), signature='s')) - script = [ - ( 'verify-match', True, 1 ) - ] - self.device_mock.SetVerifyScript(script) - - tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTHINFO_UNAVAIL) - res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ]) - - def test_pam_fprintd_retry(self): - self.setup_device() - script = [ - ( 'verify-swipe-too-short', False, 1 ), - ( 'verify-finger-not-centered', False, 1 ), - ( 'verify-match', True, 1 ) - ] - 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.assertRegex(res.errors[0], r'Swipe was too short, try again') - self.assertRegex(res.errors[1], r'Your finger was not centered, try swiping your finger again') - - def test_pam_fprintd_no_fingers_while_verifying(self): - self.setup_device() - script = [ - ( 'MOCK: no-prints', True, 1), - ( 'verify-match', True, 1 ) - ] - self.device_mock.SetVerifyScript(script) - - tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTHINFO_UNAVAIL) - res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ]) - - def test_pam_fprintd_blocks_unexpected_auth(self): - self.setup_device() - script = [ - ( 'verify-match', True, -500 ), # This one is sent before VerifyStart has completed - ( '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_MAXTRIES) - 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[0], r'Failed to match fingerprint') - self.assertRegex(res.errors[0], r'Failed to match fingerprint') - - # Check that we can cannot stop verification or release the device. i.e. - # PAM should have correctly done this after verification was done. - with self.assertRaisesRegex(dbus.exceptions.DBusException, r'net\.reactivated\.Fprint\.Error\.NoActionInProgress'): - self.device_mock.VerifyStop() - with self.assertRaisesRegex(dbus.exceptions.DBusException, r'net\.reactivated\.Fprint\.Error\.ClaimDevice'): - self.device_mock.Release() - - def test_pam_fprintd_blocks_unexpected_auth2(self): - self.setup_device() - script = [ - ( 'verify-no-match', True, 1 ), - ( 'verify-match', True, -500 ), # This one is sent before VerifyStart has completed - ( 'verify-no-match', True, 1 ), - ( 'verify-no-match', True, 1 ), - ] - self.device_mock.SetVerifyScript(script) - - tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_MAXTRIES) - 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[0], r'Failed to match fingerprint') - self.assertRegex(res.errors[0], r'Failed to match fingerprint') - - def test_pam_fprintd_dual_reader_auth(self): - device_path = self.obj_fprintd_mock.AddDevice('FDO Sandpaper Reader', 3, 'press') - sandpaper_device_mock = self.dbus_con.get_object('net.reactivated.Fprint', device_path) - sandpaper_device_mock.SetEnrolledFingers('toto', ['left-middle-finger', 'right-middle-finger']) - script = [ - ( 'verify-match', True, 2 ) - ] - sandpaper_device_mock.SetVerifyScript(script) - - # Add a 2nd device - self.setup_device() - - 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'Place your left middle finger on FDO Sandpaper Reader') - self.assertEqual(len(res.errors), 0) - - def test_pam_fprintd_multi_reader_not_all_enrolled(self): - # Add a 1st device with actual enrolled prints - device_path = self.obj_fprintd_mock.AddDevice('FDO Empty reader', 3, 'press') - empty_reader = self.dbus_con.get_object('net.reactivated.Fprint', device_path) - empty_reader.SetEnrolledFingers('toto', dbus.Array(set([]), signature='s')) - - # Add a 2nd device with actual enrolled prints - device_path = self.obj_fprintd_mock.AddDevice('FDO Most Used Reader', 3, 'press') - sandpaper_device_mock = self.dbus_con.get_object('net.reactivated.Fprint', device_path) - sandpaper_device_mock.SetEnrolledFingers('toto', ['left-middle-finger', 'right-middle-finger']) - script = [ - ( 'verify-match', True, 2 ) - ] - sandpaper_device_mock.SetVerifyScript(script) - - # Add a 3rd device, with only one enrolled finger - self.setup_device() - self.device_mock.SetEnrolledFingers('toto', ['left-middle-finger']) - - 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'Place your left middle finger on FDO Most Used Reader') - self.assertEqual(len(res.errors), 0) - - def test_pam_fprintd_last_try_auth(self): - self.setup_device() - script = [ - ( 'verify-no-match', True, 1 ), - ( 'verify-no-match', True, 1 ), - ( 'verify-match', True, 1 ), - ] - 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), 2) - self.assertRegex(res.errors[0], r'Failed to match fingerprint') - self.assertRegex(res.errors[1], r'Failed to match fingerprint') - - 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_MAXTRIES) - 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') - - def test_pam_already_claimed(self): - self.setup_device() - script = [ - ( 'verify-match', True, 2 ) - ] - self.device_mock.SetVerifyScript(script) - self.device_mock.SetClaimed('toto') - - tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTHINFO_UNAVAIL) - res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ]) - - self.assertEqual(len(res.info), 0) - self.assertEqual(len(res.errors), 0) - - def test_pam_timeout(self): - self.setup_device() - - tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTHINFO_UNAVAIL) - res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ]) - self.assertRegex(res.info[1], r'Verification timed out') - - def test_pam_notices_fprintd_disappearing(self): - self.setup_device() - - script = [ - ( 'MOCK: quit', True, 0 ), - ] - self.device_mock.SetVerifyScript(script) - - tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTHINFO_UNAVAIL) - res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ]) - self.assertEqual(len(res.errors), 0) - self.assertEqual(len(res.info), 0) - -if __name__ == '__main__': - if 'PAM_WRAPPER_SERVICE_DIR' not in os.environ: - print('Cannot run test without environment set correctly, run "meson test" instead') - sys.exit(1) - # set stream to sys.stderr to get debug output - unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2)) diff --git a/tests/prints/README b/tests/prints/README deleted file mode 100644 index 977d706..0000000 --- a/tests/prints/README +++ /dev/null @@ -1,3 +0,0 @@ -These are example images from NIST and are in the public domain. - -The PNG files have been generated by using the greyscale data as a mask. diff --git a/tests/prints/arch.jpg b/tests/prints/arch.jpg deleted file mode 100644 index b87e285..0000000 Binary files a/tests/prints/arch.jpg and /dev/null differ diff --git a/tests/prints/arch.png b/tests/prints/arch.png deleted file mode 100644 index f8c9b4d..0000000 Binary files a/tests/prints/arch.png and /dev/null differ diff --git a/tests/prints/loop-right.jpg b/tests/prints/loop-right.jpg deleted file mode 100644 index 126b90e..0000000 Binary files a/tests/prints/loop-right.jpg and /dev/null differ diff --git a/tests/prints/loop-right.png b/tests/prints/loop-right.png deleted file mode 100644 index 93177dd..0000000 Binary files a/tests/prints/loop-right.png and /dev/null differ diff --git a/tests/prints/tented_arch.jpg b/tests/prints/tented_arch.jpg deleted file mode 100644 index e73f7cc..0000000 Binary files a/tests/prints/tented_arch.jpg and /dev/null differ diff --git a/tests/prints/tented_arch.png b/tests/prints/tented_arch.png deleted file mode 100644 index c524a1f..0000000 Binary files a/tests/prints/tented_arch.png and /dev/null differ diff --git a/tests/prints/whorl.jpg b/tests/prints/whorl.jpg deleted file mode 100644 index 96c79ba..0000000 Binary files a/tests/prints/whorl.jpg and /dev/null differ diff --git a/tests/prints/whorl.png b/tests/prints/whorl.png deleted file mode 100644 index 9e7e7c4..0000000 Binary files a/tests/prints/whorl.png and /dev/null differ diff --git a/tests/test_fprintd_utils.py b/tests/test_fprintd_utils.py deleted file mode 100755 index f10d4b0..0000000 --- a/tests/test_fprintd_utils.py +++ /dev/null @@ -1,296 +0,0 @@ -#!/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 os -import time -from output_checker import OutputChecker - - -VALID_FINGER_NAMES = [ - 'left-thumb', - 'left-index-finger', - 'left-middle-finger', - 'left-ring-finger', - 'left-little-finger', - 'right-thumb', - 'right-index-finger', - 'right-middle-finger', - 'right-ring-finger', - 'right-little-finger' -] - -dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) - -class TestFprintdUtilsBase(dbusmock.DBusTestCase): - '''Test fprintd utilities''' - - @classmethod - def setUpClass(klass): - klass.start_system_bus() - klass.dbus_con = klass.get_dbus(True) - klass.sleep_time = 0.5 - - 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) - - klass.tools_prefix = '' - if 'FPRINT_BUILD_DIR' in os.environ: - klass.tools_prefix = os.environ['FPRINT_BUILD_DIR'] + '/../utils/' - print ('Using tools from %s' % klass.tools_prefix) - else: - print ('Using tools from $PATH') - - klass.wrapper_args = [] - klass.valgrind = False - if 'VALGRIND' in os.environ: - valgrind = os.environ['VALGRIND'] - if valgrind is not None: - klass.valgrind = True - klass.sleep_time *= 4 - klass.wrapper_args = ['valgrind', '--leak-check=full'] - if os.path.exists(valgrind): - klass.wrapper_args += ['--suppressions={}'.format(valgrind)] - - if 'ADDRESS_SANITIZER' in os.environ: - klass.sleep_time *= 2 - - def setUp(self): - super().setUp() - (self.p_mock, self.obj_fprintd_manager) = self.spawn_server_template( - self.template_name, {}) - # set log to nonblocking - 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() - super().tearDown() - - def setup_device(self): - 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', - self.device_path) - self.set_enrolled_fingers(['left-little-finger', 'right-little-finger']) - - def set_enrolled_fingers(self, fingers, user='toto'): - self.enrolled_fingers = fingers - self.device_mock.SetEnrolledFingers('toto', self.enrolled_fingers, - signature='sas') - - def start_utility_process(self, utility_name, args=[], sleep=True): - utility = [ os.path.join(self.tools_prefix, 'fprintd-{}'.format(utility_name)) ] - output = OutputChecker() - process = subprocess.Popen(self.wrapper_args + utility + args, - stdout=output.fd, - stderr=subprocess.STDOUT) - output.writer_attached() - - self.addCleanup(output.assert_closed) - self.addCleanup(self.try_stop_utility_process, process) - - if sleep: - time.sleep(self.sleep_time) - - return process, output - - def stop_utility_process(self, process): - process.terminate() - process.wait() - - def try_stop_utility_process(self, process): - try: - self.stop_utility_process(process) - except: - pass - - def run_utility_process(self, utility_name, args=[], sleep=True, timeout=None): - proc, output = self.start_utility_process(utility_name, args=args, sleep=sleep) - ret = proc.wait(timeout=timeout if timeout is not None else self.sleep_time * 4) - self.assertLessEqual(ret, 128) - - return b''.join(output.clear()), ret - - -class TestFprintdUtils(TestFprintdUtilsBase): - def setUp(self): - super().setUp() - self.setup_device() - - def test_fprintd_enroll(self): - process, out = self.start_utility_process('enroll', ['-f', 'right-index-finger', 'toto']) - - out.check_line(rb'right-index-finger', 0) - - self.device_mock.EmitEnrollStatus('enroll-completed', True) - - out.check_line(rb'Enroll result: enroll-completed', self.sleep_time) - - def test_fprintd_list(self): - # Rick has no fingerprints enrolled - out, ret = self.run_utility_process('list', ['rick']) - self.assertRegex(out, rb'has no fingers enrolled for') - self.assertEqual(ret, 0) - - # Toto does - out, ret = self.run_utility_process('list', ['toto']) - self.assertRegex(out, rb'right-little-finger') - self.assertEqual(ret, 0) - - def test_fprintd_delete(self): - # Delete fingerprints - out, ret = self.run_utility_process('delete', ['toto']) - self.assertRegex(out, rb'Fingerprints of user toto deleted') - self.assertEqual(ret, 0) - - # Doesn't have fingerprints - out, ret = self.run_utility_process('delete', ['toto']) - self.assertRegex(out, rb'No fingerprints to delete on') - self.assertEqual(ret, 0) - - -class TestFprintdUtilsNoDeviceTests(TestFprintdUtilsBase): - def test_fprintd_enroll(self): - out, ret = self.run_utility_process('enroll', ['toto']) - self.assertIn(b'No devices available', out) - self.assertEqual(ret, 1) - - def test_fprintd_list(self): - out, ret = self.run_utility_process('list', ['toto']) - self.assertIn(b'No devices available', out) - self.assertEqual(ret, 1) - - def test_fprintd_delete(self): - out, ret = self.run_utility_process('delete', ['toto']) - self.assertIn(b'No devices available', out) - self.assertEqual(ret, 1) - - def test_fprintd_verify(self): - out, ret = self.run_utility_process('verify', ['toto']) - self.assertIn(b'No devices available', out) - self.assertEqual(ret, 1) - - -class TestFprintdUtilsVerify(TestFprintdUtilsBase): - def setUp(self): - super().setUp() - self.setup_device() - - def start_verify_process(self, user='toto', finger=None, nowait=False): - args = [user] - if finger: - args += ['-f', finger] - - self.process, self.output = self.start_utility_process('verify', args) - if nowait: - return - - preamble = self.output.check_line(b'Verify started!') - - out = b''.join(preamble) - - self.assertNotIn(b'Verify result:', out) - - if finger: - expected_finger = finger - if finger == 'any' and not self.device_mock.HasIdentification(): - expected_finger = self.enrolled_fingers[0] - self.assertEqual(self.device_mock.GetSelectedFinger(), expected_finger) - - def assertVerifyMatch(self, match): - self.output.check_line(r'Verify result: {} (done)'.format( - 'verify-match' if match else 'verify-no-match')) - - def test_fprintd_verify(self): - self.start_verify_process() - - self.device_mock.EmitVerifyStatus('verify-match', True) - time.sleep(self.sleep_time) - self.assertVerifyMatch(True) - - def test_fprintd_verify_enrolled_fingers(self): - for finger in self.enrolled_fingers: - self.start_verify_process(finger=finger) - - self.device_mock.EmitVerifyStatus('verify-match', True) - time.sleep(self.sleep_time) - self.assertVerifyMatch(True) - - def test_fprintd_verify_any_finger_no_identification(self): - self.start_verify_process(finger='any') - - self.device_mock.EmitVerifyStatus('verify-match', True) - time.sleep(self.sleep_time) - self.assertVerifyMatch(True) - - def test_fprintd_verify_any_finger_identification(self): - self.obj_fprintd_mock.RemoveDevice(self.device_path) - self.device_path = self.obj_fprintd_mock.AddDevice('Full powered device', - 3, 'press', True) - self.device_mock = self.dbus_con.get_object('net.reactivated.Fprint', - self.device_path) - self.set_enrolled_fingers(VALID_FINGER_NAMES) - self.start_verify_process(finger='any') - - self.device_mock.EmitVerifyStatus('verify-match', True) - time.sleep(self.sleep_time) - self.assertVerifyMatch(True) - - def test_fprintd_verify_not_enrolled_fingers(self): - for finger in [f for f in VALID_FINGER_NAMES if f not in self.enrolled_fingers]: - self.start_verify_process(finger=finger, nowait=True) - regex = r'Finger \'{}\' not enrolled'.format(finger) - self.output.check_line_re(regex, timeout=self.sleep_time) - - self.device_mock.Release() - - def test_fprintd_verify_no_enrolled_fingers(self): - self.set_enrolled_fingers([]) - self.start_verify_process(nowait=True) - self.output.check_line(b'No fingers enrolled for this device.', timeout=self.sleep_time) - self.assertEqual(self.process.poll(), 1) - - def test_fprintd_list_all_fingers(self): - self.set_enrolled_fingers(VALID_FINGER_NAMES) - self.start_verify_process() - - def test_fprintd_verify_script(self): - script = [ - ( 'verify-match', True, 2 ) - ] - self.device_mock.SetVerifyScript(script) - time.sleep(2) - - self.start_verify_process() - time.sleep(2 + self.sleep_time) - self.assertVerifyMatch(True) - - def test_fprintd_multiple_verify_fails(self): - self.start_verify_process() - - self.start_verify_process(nowait=True) - self.output.check_line_re(rb'Device already in use by [A-z]+', timeout=self.sleep_time) - -if __name__ == '__main__': - # avoid writing to stderr - unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2)) diff --git a/tests/unittest_inspector.py b/tests/unittest_inspector.py deleted file mode 100755 index 0d5d3a6..0000000 --- a/tests/unittest_inspector.py +++ /dev/null @@ -1,46 +0,0 @@ -#! /usr/bin/env python3 -# Copyright © 2020, Canonical Ltd -# -# 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 2.1 of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. If not, see . -# Authors: -# Marco Trevisan - -import argparse -import importlib.util -import inspect -import os -import unittest - -def list_tests(module): - tests = [] - for name, obj in inspect.getmembers(module): - if inspect.isclass(obj) and issubclass(obj, unittest.TestCase): - cases = unittest.defaultTestLoader.getTestCaseNames(obj) - tests += [ (obj, '{}.{}'.format(name, t)) for t in cases ] - return tests - - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('unittest_source', type=argparse.FileType('r')) - - args = parser.parse_args() - source_path = args.unittest_source.name - spec = importlib.util.spec_from_file_location( - os.path.basename(source_path), source_path) - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - - for machine, human in list_tests(module): - print(human) diff --git a/utils/delete.c b/utils/delete.c deleted file mode 100644 index 07c0fd8..0000000 --- a/utils/delete.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * fprintd example to delete fingerprints - * Copyright (C) 2008 Daniel Drake - * Copyright (C) 2020 Marco Trevisan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include "fprintd-dbus.h" - -static FprintDBusManager *manager = NULL; -static GDBusConnection *connection = NULL; - -static void -create_manager (void) -{ - g_autoptr(GError) error = NULL; - - connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); - if (connection == NULL) - { - g_print ("Failed to connect to session bus: %s\n", error->message); - exit (1); - } - - manager = fprint_dbus_manager_proxy_new_sync (connection, - G_DBUS_PROXY_FLAGS_NONE, - "net.reactivated.Fprint", - "/net/reactivated/Fprint/Manager", - NULL, &error); - if (manager == NULL) - { - g_print ("Failed to get Fprintd manager: %s\n", error->message); - exit (1); - } -} - -static gboolean -delete_user_prints (FprintDBusDevice *dev, - const char *fingername, - GError **error) -{ - if (fingername) - return fprint_dbus_device_call_delete_enrolled_finger_sync (dev, fingername, - NULL, error); - else - return fprint_dbus_device_call_delete_enrolled_fingers2_sync (dev, NULL, - error); -} - -static void -delete_fingerprints (FprintDBusDevice *dev, - const char *username, - const char *fingername) -{ - g_autoptr(GError) error = NULL; - - if (!fprint_dbus_device_call_claim_sync (dev, username, NULL, &error)) - { - g_print ("failed to claim device: %s\n", error->message); - exit (1); - } - - if (!delete_user_prints (dev, fingername, &error)) - { - gboolean ignore_error = FALSE; - if (g_dbus_error_is_remote_error (error)) - { - g_autofree char *dbus_error = - g_dbus_error_get_remote_error (error); - if (g_str_equal (dbus_error, - "net.reactivated.Fprint.Error.NoEnrolledPrints")) - { - g_print ("No fingerprints to delete on %s\n", - fprint_dbus_device_get_name (dev)); - ignore_error = TRUE; - } - } - if (!ignore_error) - { - g_print ("Failed to delete fingerprints: %s\n", - error->message); - exit (1); - } - else - { - g_print ("No fingerprints to delete on %s\n", - fprint_dbus_device_get_name (dev)); - } - } - else - { - if (fingername) - g_print ("Fingerprint %s of user %s deleted on %s\n", - fingername, username, - fprint_dbus_device_get_name (dev)); - else - g_print ("Fingerprints of user %s deleted on %s\n", username, - fprint_dbus_device_get_name (dev)); - } - g_clear_error (&error); - - if (!fprint_dbus_device_call_release_sync (dev, NULL, &error)) - { - g_print ("ReleaseDevice failed: %s\n", error->message); - exit (1); - } -} - -static void -process_devices (guint argc, char **argv) -{ - g_autoptr(GOptionContext) option_context = NULL; - g_autoptr(GError) error = NULL; - g_auto(GStrv) devices = NULL; - char *fingername = NULL; - char *path; - guint num_devices; - guint i; - - if (!fprint_dbus_manager_call_get_devices_sync (manager, &devices, - NULL, &error)) - { - g_print ("Impossible to get devices: %s\n", error->message); - exit (1); - } - - num_devices = g_strv_length (devices); - if (num_devices == 0) - { - g_print ("No devices available\n"); - exit (1); - } - - g_print ("found %u devices\n", num_devices); - for (i = 0; devices[i] != NULL; i++) - { - path = devices[i]; - g_print ("Device at %s\n", path); - } - - - const GOptionEntry user_options[] = { - { "finger", 'f', 0, G_OPTION_ARG_STRING, &fingername, "Finger selected to verify (default is automatic)", NULL }, - { NULL } - }; - - option_context = g_option_context_new (NULL); - g_option_context_add_main_entries (option_context, user_options, NULL); - - for (i = 0; devices[i] != NULL; i++) - { - g_autoptr(FprintDBusDevice) dev = NULL; - guint j; - - path = devices[i]; - g_print ("Using device %s\n", path); - - /* NOTE: We should handle error cases! */ - dev = fprint_dbus_device_proxy_new_sync (connection, - G_DBUS_PROXY_FLAGS_NONE, - "net.reactivated.Fprint", - path, NULL, NULL); - - for (j = 1; argv[j] != NULL; j++) - { - const char *username = argv[j]; - fingername = NULL; - - if (argc > j + 1 && argv[j + 1][0] == '-') - { - g_autoptr(GError) local_error = NULL; - g_autoptr(GPtrArray) user_args = NULL; - - user_args = g_ptr_array_new_full (3, NULL); - g_ptr_array_add (user_args, argv[j]); - g_ptr_array_add (user_args, argv[j + 1]); - - if (argc > j + 2) - g_ptr_array_add (user_args, argv[j + 2]); - - int new_argc = user_args->len; - char **new_argv = (char **) user_args->pdata; - - if (!g_option_context_parse (option_context, &new_argc, - &new_argv, &local_error)) - { - g_print ("couldn't parse command-line options: %s\n", - local_error->message); - j += 1; - continue; - } - - j += 2; - } - - delete_fingerprints (dev, username, fingername); - } - } -} - -int -main (int argc, char **argv) -{ - g_autoptr(GOptionContext) option_context = NULL; - g_autoptr(GError) local_error = NULL; - - option_context = g_option_context_new ("Delete fingerprints"); - - setlocale (LC_ALL, ""); - - g_option_context_set_ignore_unknown_options (option_context, TRUE); - g_option_context_set_summary (option_context, - " [-f finger-name [usernames [-f finger-name ]...]"); - - if (!g_option_context_parse (option_context, &argc, &argv, &local_error)) - { - g_print ("couldn't parse command-line options: %s\n", local_error->message); - return EXIT_FAILURE; - } - - if (argc < 2) - { - g_autofree char *usage = NULL; - - usage = g_option_context_get_help (option_context, FALSE, NULL); - g_print ("%s", usage); - return EXIT_FAILURE; - } - - create_manager (); - process_devices (argc, argv); - - return 0; -} diff --git a/utils/enroll.c b/utils/enroll.c deleted file mode 100644 index aa68da7..0000000 --- a/utils/enroll.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * fprintd example to enroll right index finger - * Copyright (C) 2008 Daniel Drake - * Copyright (C) 2020 Marco Trevisan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include "fprintd-dbus.h" - -#define N_(x) x -#define TR(x) x -#include "fingerprint-strings.h" - -static FprintDBusManager *manager = NULL; -static GDBusConnection *connection = NULL; -static char *finger_name = NULL; -static char **usernames = NULL; - -typedef enum { - ENROLL_INCOMPLETE, - ENROLL_COMPLETED, - ENROLL_FAILED, -} FprintEnrollStatus; - -static void -create_manager (void) -{ - g_autoptr(GError) error = NULL; - - connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); - if (connection == NULL) - { - g_print ("Failed to connect to session bus: %s\n", error->message); - exit (1); - } - - manager = fprint_dbus_manager_proxy_new_sync (connection, - G_DBUS_PROXY_FLAGS_NONE, - "net.reactivated.Fprint", - "/net/reactivated/Fprint/Manager", - NULL, &error); - if (manager == NULL) - { - g_print ("Failed to get Fprintd manager: %s\n", error->message); - exit (1); - } -} - -static FprintDBusDevice * -open_device (const char *username) -{ - g_autoptr(FprintDBusDevice) dev = NULL; - g_autoptr(GError) error = NULL; - g_autofree char *path = NULL; - - if (!fprint_dbus_manager_call_get_default_device_sync (manager, &path, - NULL, &error)) - { - g_print ("Impossible to enroll: %s\n", error->message); - exit (1); - } - - g_print ("Using device %s\n", path); - - dev = fprint_dbus_device_proxy_new_sync (connection, - G_DBUS_PROXY_FLAGS_NONE, - "net.reactivated.Fprint", - path, NULL, &error); - - if (error) - { - g_print ("failed to connect to device: %s\n", error->message); - exit (1); - } - - if (!fprint_dbus_device_call_claim_sync (dev, username, NULL, &error)) - { - g_print ("failed to claim device: %s\n", error->message); - exit (1); - } - return g_steal_pointer (&dev); -} - -static void -enroll_result (GObject *object, const char *result, gboolean done, void *user_data) -{ - FprintEnrollStatus *enroll_status = user_data; - - g_print ("Enroll result: %s\n", result); - if (done != FALSE) - { - if (g_str_equal (result, "enroll-completed")) - *enroll_status = ENROLL_COMPLETED; - else - *enroll_status = ENROLL_FAILED; - } -} - -static void -proxy_signal_cb (GDBusProxy *proxy, - const gchar *sender_name, - const gchar *signal_name, - GVariant *parameters, - gpointer user_data) -{ - if (g_str_equal (signal_name, "EnrollStatus")) - { - const gchar *result; - gboolean done; - - g_variant_get (parameters, "(&sb)", &result, &done); - enroll_result (G_OBJECT (proxy), result, done, user_data); - } -} - -static FprintEnrollStatus -do_enroll (FprintDBusDevice *dev) -{ - g_autoptr(GError) error = NULL; - FprintEnrollStatus enroll_status = ENROLL_INCOMPLETE; - gboolean found; - guint i; - - g_signal_connect (dev, "g-signal", G_CALLBACK (proxy_signal_cb), - &enroll_status); - - found = FALSE; - for (i = 0; fingers[i].dbus_name != NULL; i++) - { - if (g_strcmp0 (fingers[i].dbus_name, finger_name) == 0) - { - found = TRUE; - break; - } - } - if (!found) - { - g_autoptr(GString) s = NULL; - - s = g_string_new (NULL); - g_string_append_printf (s, "Invalid finger name '%s'. Name must be one of ", finger_name); - for (i = 0; fingers[i].dbus_name != NULL; i++) - { - g_string_append_printf (s, "%s", fingers[i].dbus_name); - if (fingers[i + 1].dbus_name != NULL) - g_string_append (s, ", "); - } - g_warning ("%s", s->str); - exit (1); - } - - g_print ("Enrolling %s finger.\n", finger_name); - if (!fprint_dbus_device_call_enroll_start_sync (dev, finger_name, NULL, - &error)) - { - g_print ("EnrollStart failed: %s\n", error->message); - exit (1); - } - - while (enroll_status == ENROLL_INCOMPLETE) - g_main_context_iteration (NULL, TRUE); - - g_signal_handlers_disconnect_by_func (dev, proxy_signal_cb, &enroll_status); - - if (!fprint_dbus_device_call_enroll_stop_sync (dev, NULL, &error)) - { - g_print ("EnrollStop failed: %s\n", error->message); - exit (1); - } - - return enroll_status; -} - -static void -release_device (FprintDBusDevice *dev) -{ - g_autoptr(GError) error = NULL; - if (!fprint_dbus_device_call_release_sync (dev, NULL, &error)) - { - g_print ("ReleaseDevice failed: %s\n", error->message); - exit (1); - } -} - -static const GOptionEntry entries[] = { - { "finger", 'f', 0, G_OPTION_ARG_STRING, &finger_name, "Finger selected to verify (default is automatic)", NULL }, - { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &usernames, NULL, "[username]" }, - { NULL } -}; - -int -main (int argc, char **argv) -{ - g_autoptr(FprintDBusDevice) dev = NULL; - GOptionContext *context; - FprintEnrollStatus status; - - g_autoptr(GError) err = NULL; - - setlocale (LC_ALL, ""); - - context = g_option_context_new ("Enroll a fingerprint"); - g_option_context_add_main_entries (context, entries, NULL); - - if (g_option_context_parse (context, &argc, &argv, &err) == FALSE) - { - g_print ("couldn't parse command-line options: %s\n", err->message); - return 1; - } - - if (finger_name == NULL) - finger_name = g_strdup ("right-index-finger"); - - create_manager (); - - dev = open_device (usernames ? usernames[0] : ""); - status = do_enroll (dev); - release_device (dev); - g_free (finger_name); - g_strfreev (usernames); - return status == ENROLL_COMPLETED ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/utils/list.c b/utils/list.c deleted file mode 100644 index df6422c..0000000 --- a/utils/list.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * fprintd example to list enrolled fingerprints - * Copyright (C) 2008 Daniel Drake - * Copyright (C) 2020 Marco Trevisan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include "fprintd-dbus.h" - -static FprintDBusManager *manager = NULL; -static GDBusConnection *connection = NULL; - -static void -create_manager (void) -{ - g_autoptr(GError) error = NULL; - - connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); - if (connection == NULL) - { - g_print ("Failed to connect to session bus: %s\n", error->message); - exit (1); - } - - manager = fprint_dbus_manager_proxy_new_sync (connection, - G_DBUS_PROXY_FLAGS_NONE, - "net.reactivated.Fprint", - "/net/reactivated/Fprint/Manager", - NULL, &error); - if (manager == NULL) - { - g_print ("Failed to get Fprintd manager: %s\n", error->message); - exit (1); - } -} - -static void -list_fingerprints (FprintDBusDevice *dev, const char *username) -{ - g_autoptr(GError) error = NULL; - g_auto(GStrv) fingers = NULL; - guint i; - - if (!fprint_dbus_device_call_list_enrolled_fingers_sync (dev, username, - &fingers, NULL, - &error)) - { - gboolean ignore_error = FALSE; - if (g_dbus_error_is_remote_error (error)) - { - g_autofree char *dbus_error = - g_dbus_error_get_remote_error (error); - if (g_str_equal (dbus_error, - "net.reactivated.Fprint.Error.NoEnrolledPrints")) - ignore_error = TRUE; - } - - if (!ignore_error) - { - g_print ("ListEnrolledFingers failed: %s\n", error->message); - exit (1); - } - } - - if (fingers == NULL || g_strv_length (fingers) == 0) - { - g_print ("User %s has no fingers enrolled for %s.\n", username, - fprint_dbus_device_get_name (dev)); - return; - } - - g_print ("Fingerprints for user %s on %s (%s):\n", - username, - fprint_dbus_device_get_name (dev), - fprint_dbus_device_get_scan_type (dev)); - - for (i = 0; fingers[i] != NULL; i++) - g_print (" - #%d: %s\n", i, fingers[i]); -} - -static void -process_devices (char **argv) -{ - g_auto(GStrv) devices = NULL; - g_autoptr(GError) error = NULL; - char *path; - guint num_devices; - guint i; - - if (!fprint_dbus_manager_call_get_devices_sync (manager, &devices, NULL, - &error)) - { - g_print ("Impossible to get devices: %s\n", error->message); - exit (1); - } - - num_devices = g_strv_length (devices); - if (num_devices == 0) - { - g_print ("No devices available\n"); - exit (1); - } - - g_print ("found %u devices\n", num_devices); - for (i = 0; devices[i] != NULL; i++) - { - path = devices[i]; - g_print ("Device at %s\n", path); - } - - for (i = 0; devices[i] != NULL; i++) - { - g_autoptr(FprintDBusDevice) dev = NULL; - guint j; - - path = devices[i]; - g_print ("Using device %s\n", path); - - /* NOTE: We should handle error cases! */ - dev = fprint_dbus_device_proxy_new_sync (connection, - G_DBUS_PROXY_FLAGS_NONE, - "net.reactivated.Fprint", - path, NULL, NULL); - - for (j = 1; argv[j] != NULL; j++) - list_fingerprints (dev, argv[j]); - } -} - -int -main (int argc, char **argv) -{ - setlocale (LC_ALL, ""); - - create_manager (); - - if (argc < 2) - { - g_print ("Usage: %s [usernames...]\n", argv[0]); - return 1; - } - - process_devices (argv); - - return 0; -} diff --git a/utils/meson.build b/utils/meson.build deleted file mode 100644 index 4056c79..0000000 --- a/utils/meson.build +++ /dev/null @@ -1,34 +0,0 @@ -libfprintd_utils_dep = declare_dependency( - include_directories: [ - include_directories('../src'), - include_directories('../pam'), - ], - dependencies: [ - glib_dep, - gio_dep, - gio_unix_dep, - ], - sources: [ - fprintd_dbus_sources, - ], - link_with: [ - libfprintd_private - ], -) - -utils = [ - 'delete', - 'enroll', - 'list', - 'verify', -] - -fprintd_utils = [] - -foreach util: utils - fprintd_utils += executable('fprintd-' + util, - sources: util + '.c', - dependencies: libfprintd_utils_dep, - install: true, - ) -endforeach diff --git a/utils/verify.c b/utils/verify.c deleted file mode 100644 index d49fdf8..0000000 --- a/utils/verify.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * fprintd example to verify a fingerprint - * Copyright (C) 2008 Daniel Drake - * Copyright (C) 2020 Marco Trevisan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include -#include "fprintd-dbus.h" - -static FprintDBusManager *manager = NULL; -static GDBusConnection *connection = NULL; -static char *finger_name = NULL; -static gboolean g_fatal_warnings = FALSE; -static char **usernames = NULL; - -static void -create_manager (void) -{ - g_autoptr(GError) error = NULL; - - connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); - if (connection == NULL) - { - g_print ("Failed to connect to session bus: %s\n", error->message); - exit (1); - } - - manager = fprint_dbus_manager_proxy_new_sync (connection, - G_DBUS_PROXY_FLAGS_NONE, - "net.reactivated.Fprint", - "/net/reactivated/Fprint/Manager", - NULL, &error); - if (manager == NULL) - { - g_print ("Failed to get Fprintd manager: %s\n", error->message); - exit (1); - } -} - -static FprintDBusDevice * -open_device (const char *username) -{ - g_autoptr(FprintDBusDevice) dev = NULL; - g_autoptr(GError) error = NULL; - g_autofree char *path = NULL; - - if (!fprint_dbus_manager_call_get_default_device_sync (manager, &path, - NULL, &error)) - { - g_print ("Impossible to verify: %s\n", error->message); - exit (1); - } - - g_print ("Using device %s\n", path); - - dev = fprint_dbus_device_proxy_new_sync (connection, - G_DBUS_PROXY_FLAGS_NONE, - "net.reactivated.Fprint", - path, NULL, &error); - - if (error) - { - g_print ("failed to connect to device: %s\n", error->message); - exit (1); - } - - if (!fprint_dbus_device_call_claim_sync (dev, username, NULL, &error)) - { - g_print ("failed to claim device: %s\n", error->message); - exit (1); - } - - return g_steal_pointer (&dev); -} - -static void -find_finger (FprintDBusDevice *dev, const char *username) -{ - g_autoptr(GError) error = NULL; - g_auto(GStrv) fingers = NULL; - guint i; - - if (!fprint_dbus_device_call_list_enrolled_fingers_sync (dev, username, - &fingers, - NULL, &error)) - { - g_print ("ListEnrolledFingers failed: %s\n", error->message); - exit (1); - } - - if (fingers == NULL || g_strv_length (fingers) == 0) - { - g_print ("No fingers enrolled for this device.\n"); - exit (1); - } - - g_print ("Listing enrolled fingers:\n"); - for (i = 0; fingers[i] != NULL; i++) - g_print (" - #%d: %s\n", i, fingers[i]); - - if (finger_name && !g_str_equal (finger_name, "any") && - !g_strv_contains ((const char **) fingers, finger_name)) - { - g_print ("Finger '%s' not enrolled for user %s.\n", finger_name, - username); - g_free (finger_name); - exit (1); - } - - if (finger_name == NULL) - finger_name = g_strdup (fingers[0]); -} - -struct VerifyState -{ - GError *error; - gboolean started; - gboolean completed; - gboolean match; -}; - -static void -verify_result (GObject *object, const char *result, gboolean done, void *user_data) -{ - struct VerifyState *verify_state = user_data; - - g_print ("Verify result: %s (%s)\n", result, done ? "done" : "not done"); - verify_state->match = g_str_equal (result, "verify-match"); - - if (done != FALSE) - verify_state->completed = TRUE; -} - -static void -verify_finger_selected (GObject *object, const char *name, void *user_data) -{ - g_print ("Verifying: %s\n", name); -} - -static void -verify_started_cb (GObject *obj, - GAsyncResult *res, - gpointer user_data) -{ - struct VerifyState *verify_state = user_data; - - if (fprint_dbus_device_call_verify_start_finish (FPRINT_DBUS_DEVICE (obj), res, &verify_state->error)) - { - g_print ("Verify started!\n"); - verify_state->started = TRUE; - } -} - -static void -proxy_signal_cb (GDBusProxy *proxy, - const gchar *sender_name, - const gchar *signal_name, - GVariant *parameters, - gpointer user_data) -{ - struct VerifyState *verify_state = user_data; - - if (!verify_state->started) - return; - - if (g_str_equal (signal_name, "VerifyStatus")) - { - const gchar *result; - gboolean done; - - g_variant_get (parameters, "(&sb)", &result, &done); - verify_result (G_OBJECT (proxy), result, done, user_data); - } - else if (g_str_equal (signal_name, "VerifyFingerSelected")) - { - const gchar *name; - - g_variant_get (parameters, "(&s)", &name); - verify_finger_selected (G_OBJECT (proxy), name, user_data); - } -} - -static gboolean -do_verify (FprintDBusDevice *dev) -{ - g_autoptr(GError) error = NULL; - struct VerifyState verify_state = { 0 }; - - /* This one is funny. We connect to the signal immediately to avoid - * race conditions. However, we must ignore any authentication results - * that happen before our start call returns. - * This is because the verify call itself may internally try to verify - * against fprintd (possibly using a separate account). - * - * To do so, we *must* use the async version of the verify call, as the - * sync version would cause the signals to be queued and only processed - * after it returns. - */ - - g_signal_connect (dev, "g-signal", G_CALLBACK (proxy_signal_cb), - &verify_state); - - fprint_dbus_device_call_verify_start (dev, finger_name, NULL, - verify_started_cb, - &verify_state); - - /* Wait for verify start while discarding any VerifyStatus signals */ - while (!verify_state.started && !verify_state.error) - g_main_context_iteration (NULL, TRUE); - - if (verify_state.error) - { - g_print ("VerifyStart failed: %s\n", verify_state.error->message); - g_clear_error (&verify_state.error); - exit (1); - } - - /* VerifyStatus signals are processing, wait for completion. */ - while (!verify_state.completed) - g_main_context_iteration (NULL, TRUE); - - - g_signal_handlers_disconnect_by_func (dev, proxy_signal_cb, - &verify_state); - - if (!fprint_dbus_device_call_verify_stop_sync (dev, NULL, &error)) - { - g_print ("VerifyStop failed: %s\n", error->message); - exit (1); - } - - return verify_state.match; -} - -static void -release_device (FprintDBusDevice *dev) -{ - g_autoptr(GError) error = NULL; - if (!fprint_dbus_device_call_release_sync (dev, NULL, &error)) - { - g_print ("ReleaseDevice failed: %s\n", error->message); - exit (1); - } -} - -static const GOptionEntry entries[] = { - { "finger", 'f', 0, G_OPTION_ARG_STRING, &finger_name, "Finger selected to verify (default is automatic)", NULL }, - {"g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &g_fatal_warnings, "Make all warnings fatal", NULL}, - { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &usernames, NULL, "[username]" }, - { NULL } -}; - -int -main (int argc, char **argv) -{ - g_autoptr(FprintDBusDevice) dev = NULL; - g_autoptr(GError) err = NULL; - GOptionContext *context; - const char *username = NULL; - gboolean match; - - setlocale (LC_ALL, ""); - - context = g_option_context_new ("Verify a fingerprint"); - g_option_context_add_main_entries (context, entries, NULL); - - if (g_option_context_parse (context, &argc, &argv, &err) == FALSE) - { - g_print ("couldn't parse command-line options: %s\n", err->message); - return 1; - } - - if (usernames == NULL) - username = ""; - else - username = usernames[0]; - - if (g_fatal_warnings) - { - GLogLevelFlags fatal_mask; - - fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK); - fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL; - g_log_set_always_fatal (fatal_mask); - } - - create_manager (); - - dev = open_device (username); - find_finger (dev, username); - match = do_verify (dev); - release_device (dev); - return match ? EXIT_SUCCESS : EXIT_FAILURE; -}