# -*- 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 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='sis', out_signature='s') def AddDevice(self, device_name, num_enroll_stages, scan_type): '''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.UInt32(num_enroll_stages, variant_level=1), 'scan-type': scan_type } self.AddObject(path, DEVICE_IFACE, # Properties device_properties, # Methods [ ('ListEnrolledFingers', 's', 'as', ListEnrolledFingers), ('DeleteEnrolledFingers', 's', '', DeleteEnrolledFingers), ('DeleteEnrolledFingers2', '', '', DeleteEnrolledFingers2), ('Claim', 's', '', Claim), ('Release', '', '', Release), ('VerifyStart', 's', '', VerifyStart), ('VerifyStop', '', '', VerifyStop), ('EnrollStart', 's', '', EnrollStart), ('EnrollStop', '', '', EnrollStop) ]) device = mockobject.objects[path] device.fingers = {} device.claimed_user = None device.action = None device.verify_script = [] return 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') 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 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 async def send_verify_script(device, script): for [result, done, timeout] in device.verify_script: await asyncio.sleep(timeout) 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': finger_name = device.fingers[device.claimed_user][0] device.EmitSignal(DEVICE_IFACE, 'VerifyFingerSelected', 's', [ finger_name ]) if device.verify_script is not None and len(device.verify_script) > 0: asyncio.run(send_verify_script(device, device.verify_script)) @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 @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. ''' if len(fingers) < 1: raise dbus.exceptions.DBusException( 'Fingers array must not be empty', name='org.freedesktop.DBus.Error.InvalidArgs') 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='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