device: Add duplicate checking during enroll

Always do an identify step before starting an enroll. If we find an
existing print, delete or throw an error depending on what is
appropriate.

Doing this ensures that we should not get duplicate prints system wide.
This means we will be able to identify the user that is trying to log
in. But more importantly, we need to do these checks for MoC devices,
which always run "identify" against all device stored prints rather than
the passed gallery.
This commit is contained in:
Benjamin Berg
2020-12-15 15:22:11 +01:00
committed by Marco Trevisan (Treviño)
parent b7b91e77bb
commit 5785dc65b4
2 changed files with 131 additions and 12 deletions

View File

@ -306,7 +306,8 @@ on_nr_enroll_stages_changed (FprintDevice *rdev,
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. */
nr_enroll_stages = fp_device_get_nr_enroll_stages (device) + 1;
g_debug ("Device %s enroll stages changed to %d",
fp_device_get_name (device),
@ -1637,6 +1638,7 @@ enroll_progress_cb (FpDevice *dev,
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);
}
@ -1827,6 +1829,103 @@ enroll_cb (FpDevice *dev, GAsyncResult *res, void *user_data)
stoppable_action_completed (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_storage (priv->dev))
{
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.");
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. */
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 gboolean
fprint_device_enroll_start (FprintDBusDevice *dbus_dev,
@ -1834,6 +1933,7 @@ fprint_device_enroll_start (FprintDBusDevice *dbus_dev,
const char *finger_name)
{
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) all_prints = NULL;
FprintDevice *rdev = FPRINT_DEVICE (dbus_dev);
FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev);
FpFinger finger = finger_name_to_fp_finger (finger_name);
@ -1862,17 +1962,25 @@ fprint_device_enroll_start (FprintDBusDevice *dbus_dev,
priv->current_cancellable = g_cancellable_new ();
priv->enroll_data = finger;
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);
priv->current_action = ACTION_ENROLL;
/* 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).
*/
all_prints = load_all_prints (rdev);
fp_device_identify (priv->dev,
all_prints,
priv->current_cancellable,
NULL,
NULL,
NULL,
(GAsyncReadyCallback) enroll_identify_cb,
rdev);
fprint_dbus_device_complete_enroll_start (dbus_dev, invocation);
return TRUE;

View File

@ -882,9 +882,10 @@ class FPrintdVirtualStorageDeviceBaseTest(FPrintdVirtualDeviceBaseTest):
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
stages += 1 # Adding the extra stage for duplicates-check
if self.num_enroll_stages == stages:
return
self.send_command('SET_ENROLL_STAGES', stages)
self.send_command('SET_ENROLL_STAGES', stages - 1)
while self.num_enroll_stages != stages:
ctx.iteration(True)
self.assertIn({'num-enroll-stages': stages}, self._changed_properties)
@ -918,6 +919,7 @@ class FPrintdVirtualStorageDeviceTests(FPrintdVirtualStorageDeviceBaseTest):
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()
@ -1003,6 +1005,10 @@ class FPrintdVirtualNoStorageDeviceBaseTest(FPrintdVirtualStorageDeviceBaseTest)
device_driver = 'virtual_device'
driver_name = 'Virtual device for debugging'
def enroll_image(self, img, device=None, finger='right-index-finger',
expected_result='enroll-completed', claim_user=None):
self.skipTest('Identification not supported, thus is the enrolling')
class FPrintdVirtualNoStorageDeviceTest(FPrintdVirtualNoStorageDeviceBaseTest):
def check_verify_finger_match(self, image, expect_match, finger):
@ -1107,7 +1113,7 @@ class FPrintdVirtualDeviceTest(FPrintdVirtualDeviceBaseTest):
self.driver_name)
def test_enroll_stages_property(self):
self.assertEqual(self.device.get_cached_property('num-enroll-stages').unpack(), 5)
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(),
@ -2206,6 +2212,8 @@ class FPrintdVirtualDeviceEnrollTests(FPrintdVirtualDeviceBaseTest):
self.assertEnrollError(FPrint.DeviceError.DATA_INVALID, 'enroll-unknown-error')
def test_enroll_error_data_not_found(self):
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):
@ -3009,6 +3017,9 @@ class FPrintdUtilsTest(FPrintdVirtualStorageDeviceBaseTest):
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())