Support config files for SID mapping.
This allows interoperability with Windows.
This commit is contained in:
parent
9898b5d80a
commit
d2944ba762
10 changed files with 122 additions and 52 deletions
18
README.md
18
README.md
|
|
@ -57,6 +57,24 @@ auth [success=2 default=ignore] pam_fprintd.so max_tries=1 timeout=10 # debug
|
||||||
auth [success=1 default=ignore] pam_unix.so nullok_secure try_first_pass
|
auth [success=1 default=ignore] pam_unix.so nullok_secure try_first_pass
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Windows interoperability
|
||||||
|
|
||||||
|
Note: This section is likely only relevant if you will be dual booting.
|
||||||
|
|
||||||
|
To be able to use the same set of fingerprints for Windows and Linux, you first
|
||||||
|
need to extract the Windows user IDs (known as SIDs). To do this, start Windows,
|
||||||
|
start `cmd.exe` and run `wmic useraccount get name,sid`. This will provide a
|
||||||
|
list of all users and the corresponding SIDs.
|
||||||
|
|
||||||
|
You can then create a mapping from the Linux user names (as written in the
|
||||||
|
first `:`-separated field of `/etc/passwd`). This mapping is defined in
|
||||||
|
`/etc/python-validity/dbus-service.yaml`. For example:
|
||||||
|
```yaml
|
||||||
|
user_to_sid:
|
||||||
|
"myusername": "S-1-5-21-1234567890-1234567890-1234567890-1001"
|
||||||
|
"someotheruser": "S-1-5-21-1234567890-1234567890-1234567890-1003"
|
||||||
|
```
|
||||||
|
|
||||||
## Playground
|
## Playground
|
||||||
|
|
||||||
This package contains a set of scripts you can use to do a low-level debugging of the sensor protocol.
|
This package contains a set of scripts you can use to do a low-level debugging of the sensor protocol.
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,23 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import signal
|
|
||||||
import argparse
|
import argparse
|
||||||
import re
|
|
||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
|
import re
|
||||||
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import typing
|
||||||
|
from binascii import hexlify, unhexlify
|
||||||
|
from pathlib import Path
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
import dbus
|
import dbus
|
||||||
import dbus.mainloop.glib
|
import dbus.mainloop.glib
|
||||||
import dbus.service
|
import dbus.service
|
||||||
|
import yaml
|
||||||
from usb import core as usb_core
|
from usb import core as usb_core
|
||||||
from binascii import hexlify, unhexlify
|
|
||||||
from threading import Thread
|
|
||||||
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
||||||
from gi.repository import GLib
|
from gi.repository import GLib
|
||||||
import logging
|
import logging
|
||||||
|
|
@ -23,42 +28,53 @@ from validitysensor.tls import tls
|
||||||
from validitysensor.usb import usb
|
from validitysensor.usb import usb
|
||||||
from validitysensor.sid import sid_from_string
|
from validitysensor.sid import sid_from_string
|
||||||
from validitysensor.db import subtype_to_string, db
|
from validitysensor.db import subtype_to_string, db
|
||||||
from validitysensor.sensor import sensor, reboot, RebootException
|
from validitysensor.sensor import sensor, RebootException
|
||||||
|
|
||||||
GLib.threads_init()
|
GLib.threads_init()
|
||||||
|
|
||||||
INTERFACE_NAME='io.github.uunicorn.Fprint.Device'
|
INTERFACE_NAME = 'io.github.uunicorn.Fprint.Device'
|
||||||
|
|
||||||
loop = GLib.MainLoop()
|
loop = GLib.MainLoop()
|
||||||
|
|
||||||
|
|
||||||
class NoEnrolledPrints(dbus.DBusException):
|
class NoEnrolledPrints(dbus.DBusException):
|
||||||
_dbus_error_name = 'net.reactivated.Fprint.Error.NoEnrolledPrints'
|
_dbus_error_name = 'net.reactivated.Fprint.Error.NoEnrolledPrints'
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__('No enrolled prints found')
|
super().__init__('No enrolled prints found')
|
||||||
|
|
||||||
def uid2identity(uid):
|
|
||||||
sidstr='S-1-5-21-111111111-1111111111-1111111111-%d' % uid
|
|
||||||
return sid_from_string(sidstr)
|
|
||||||
|
|
||||||
class Device(dbus.service.Object):
|
class Device(dbus.service.Object):
|
||||||
def __init__(self, bus_name):
|
def __init__(self, bus_name: dbus.Bus, config: typing.Dict[str, typing.Any]):
|
||||||
dbus.service.Object.__init__(self, bus_name, '/io/github/uunicorn/Fprint/Device')
|
dbus.service.Object.__init__(self, bus_name, '/io/github/uunicorn/Fprint/Device')
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
def user2identity(self, user: str, uid: int):
|
||||||
|
"""Compute UID to SID mapping"""
|
||||||
|
if user in self.config['user_to_sid']:
|
||||||
|
sidstr = self.config['user_to_sid'][user]
|
||||||
|
else:
|
||||||
|
sidstr = 'S-1-5-21-111111111-1111111111-1111111111-%d' % uid
|
||||||
|
return sid_from_string(sidstr)
|
||||||
|
|
||||||
|
def _get_user_sid(self, user: str):
|
||||||
|
"""Resolve user name to UID and SID"""
|
||||||
|
pw = pwd.getpwnam(user)
|
||||||
|
uid = pw.pw_uid
|
||||||
|
return db.lookup_user(self.user2identity(user, uid))
|
||||||
|
|
||||||
@dbus.service.method(dbus_interface=INTERFACE_NAME,
|
@dbus.service.method(dbus_interface=INTERFACE_NAME,
|
||||||
in_signature="s",
|
in_signature="s",
|
||||||
out_signature="as")
|
out_signature="as")
|
||||||
def ListEnrolledFingers(self, usr):
|
def ListEnrolledFingers(self, user):
|
||||||
try:
|
try:
|
||||||
logging.debug('In ListEnrolledFingers %s' % usr)
|
logging.debug('In ListEnrolledFingers %s' % user)
|
||||||
|
|
||||||
pw=pwd.getpwnam(usr)
|
usr = self._get_user_sid(user)
|
||||||
uid=pw.pw_uid
|
|
||||||
usr=db.lookup_user(uid2identity(uid))
|
|
||||||
|
|
||||||
if usr == None:
|
if usr is None:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
rc = [subtype_to_string(f['subtype']) for f in usr.fingers]
|
rc = [subtype_to_string(f['subtype']) for f in usr.fingers]
|
||||||
logging.debug(repr(rc))
|
logging.debug(repr(rc))
|
||||||
return rc
|
return rc
|
||||||
|
|
@ -66,14 +82,13 @@ class Device(dbus.service.Object):
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
@dbus.service.method(dbus_interface=INTERFACE_NAME,
|
@dbus.service.method(dbus_interface=INTERFACE_NAME,
|
||||||
in_signature='s',
|
in_signature='s',
|
||||||
out_signature='')
|
out_signature='')
|
||||||
def DeleteEnrolledFingers(self, user):
|
def DeleteEnrolledFingers(self, user):
|
||||||
logging.debug('In DeleteEnrolledFingers %s' % user)
|
logging.debug('In DeleteEnrolledFingers %s' % user)
|
||||||
pw=pwd.getpwnam(user)
|
usr = self._get_user_sid(user)
|
||||||
usr=db.lookup_user(uid2identity(pw.pw_uid))
|
|
||||||
|
|
||||||
if usr == None:
|
if usr is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
db.del_record(usr.dbid)
|
db.del_record(usr.dbid)
|
||||||
|
|
@ -84,10 +99,9 @@ class Device(dbus.service.Object):
|
||||||
def VerifyStart(self, user, finger):
|
def VerifyStart(self, user, finger):
|
||||||
logging.debug('In VerifyStart for %s, %s' % (user, finger))
|
logging.debug('In VerifyStart for %s, %s' % (user, finger))
|
||||||
|
|
||||||
pw = pwd.getpwnam(user)
|
usr = self._get_user_sid(user)
|
||||||
usr = db.lookup_user(uid2identity(pw.pw_uid))
|
|
||||||
|
|
||||||
if usr == None:
|
if usr is None:
|
||||||
raise NoEnrolledPrints()
|
raise NoEnrolledPrints()
|
||||||
|
|
||||||
self.VerifyFingerSelected('any')
|
self.VerifyFingerSelected('any')
|
||||||
|
|
@ -114,7 +128,6 @@ class Device(dbus.service.Object):
|
||||||
thread = Thread(target=run)
|
thread = Thread(target=run)
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
|
|
||||||
@dbus.service.method(dbus_interface=INTERFACE_NAME,
|
@dbus.service.method(dbus_interface=INTERFACE_NAME,
|
||||||
in_signature='',
|
in_signature='',
|
||||||
|
|
@ -127,8 +140,9 @@ class Device(dbus.service.Object):
|
||||||
out_signature='')
|
out_signature='')
|
||||||
def EnrollStart(self, user, finger_name):
|
def EnrollStart(self, user, finger_name):
|
||||||
logging.debug('In EnrollStart %s for %s' % (finger_name, user))
|
logging.debug('In EnrollStart %s for %s' % (finger_name, user))
|
||||||
pw=pwd.getpwnam(user)
|
|
||||||
uid=pw.pw_uid
|
usr = self._get_user_sid(user)
|
||||||
|
|
||||||
def update_cb(rsp, e):
|
def update_cb(rsp, e):
|
||||||
if e is not None:
|
if e is not None:
|
||||||
self.EnrollStatus('enroll-retry-scan', False)
|
self.EnrollStatus('enroll-retry-scan', False)
|
||||||
|
|
@ -137,7 +151,7 @@ class Device(dbus.service.Object):
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
try:
|
try:
|
||||||
sensor.enroll(uid2identity(uid), 0xf5, update_cb) # TODO parse the finger name
|
sensor.enroll(usr, 0xf5, update_cb) # TODO parse the finger name
|
||||||
self.EnrollStatus('enroll-completed', True)
|
self.EnrollStatus('enroll-completed', True)
|
||||||
except usb_core.USBError as e:
|
except usb_core.USBError as e:
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
|
|
@ -150,7 +164,6 @@ class Device(dbus.service.Object):
|
||||||
thread = Thread(target=run)
|
thread = Thread(target=run)
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
|
|
||||||
@dbus.service.signal(dbus_interface=INTERFACE_NAME, signature='sb')
|
@dbus.service.signal(dbus_interface=INTERFACE_NAME, signature='sb')
|
||||||
def VerifyStatus(self, result, done):
|
def VerifyStatus(self, result, done):
|
||||||
|
|
@ -171,7 +184,9 @@ class Device(dbus.service.Object):
|
||||||
logging.debug('RunCmd')
|
logging.debug('RunCmd')
|
||||||
return hexlify(tls.app(unhexlify(cmd))).decode()
|
return hexlify(tls.app(unhexlify(cmd))).decode()
|
||||||
|
|
||||||
backoff_file='/usr/share/python-validity/backoff'
|
|
||||||
|
backoff_file = '/usr/share/python-validity/backoff'
|
||||||
|
|
||||||
|
|
||||||
# I don't know how to tell systemd to backoff in case of multiple instance of the same template service, help!
|
# I don't know how to tell systemd to backoff in case of multiple instance of the same template service, help!
|
||||||
def backoff():
|
def backoff():
|
||||||
|
|
@ -179,10 +194,10 @@ def backoff():
|
||||||
with open(backoff_file, 'r') as f:
|
with open(backoff_file, 'r') as f:
|
||||||
lines = list(map(float, f.readlines()))
|
lines = list(map(float, f.readlines()))
|
||||||
else:
|
else:
|
||||||
lines=[]
|
lines = []
|
||||||
|
|
||||||
while len(lines) > 0 and lines[0] + 60 < time.time():
|
while len(lines) > 0 and lines[0] + 60 < time.time():
|
||||||
lines=lines[1:]
|
lines = lines[1:]
|
||||||
|
|
||||||
lines += [time.time()]
|
lines += [time.time()]
|
||||||
|
|
||||||
|
|
@ -193,10 +208,12 @@ def backoff():
|
||||||
if len(lines) > 10:
|
if len(lines) > 10:
|
||||||
raise Exception('Refusing to start more than 10 times per minute')
|
raise Exception('Refusing to start more than 10 times per minute')
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
def main():
|
||||||
parser = argparse.ArgumentParser('Open fprintd DBus service')
|
parser = argparse.ArgumentParser('Open fprintd DBus service')
|
||||||
parser.add_argument('--debug', help='Enable tracing', action='store_true')
|
parser.add_argument('--debug', help='Enable tracing', action='store_true')
|
||||||
parser.add_argument('--devpath', help='USB device path: usb-<busnum>-<address>')
|
parser.add_argument('--devpath', help='USB device path: usb-<busnum>-<address>')
|
||||||
|
parser.add_argument('--configpath', default='/etc/python-validity', type=Path, help='Path to config files')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.debug:
|
if args.debug:
|
||||||
|
|
@ -206,16 +223,34 @@ if __name__ == '__main__':
|
||||||
else:
|
else:
|
||||||
level = logging.INFO
|
level = logging.INFO
|
||||||
|
|
||||||
handler = logging.handlers.SysLogHandler(address = '/dev/log')
|
handler = logging.handlers.SysLogHandler(address='/dev/log')
|
||||||
logging.basicConfig(level=level, handlers=[handler])
|
logging.basicConfig(level=level, handlers=[handler])
|
||||||
|
|
||||||
|
# Load and perform basic validation of config file.
|
||||||
|
try:
|
||||||
|
with (args.configpath / 'dbus-service.yaml').open(mode='rt') as configfd:
|
||||||
|
config = yaml.load(configfd)
|
||||||
|
except FileNotFoundError:
|
||||||
|
# No configuration file. Create default
|
||||||
|
config = {'user_to_sid': {}}
|
||||||
|
|
||||||
|
if 'user_to_sid' not in config:
|
||||||
|
raise Exception("No user to SID mapping in config file!")
|
||||||
|
# Be kind to user, and allow empty mapping that got turned into a null by mistake.
|
||||||
|
if config['user_to_sid'] is None:
|
||||||
|
config['user_to_sid'] = {}
|
||||||
|
if not all(isinstance(e, str) for e in config['user_to_sid'].keys()):
|
||||||
|
raise Exception("Keys in user to SID map must be strings")
|
||||||
|
if not all(isinstance(e, str) for e in config['user_to_sid'].values()):
|
||||||
|
raise Exception("Values in user to SID map must be strings")
|
||||||
|
|
||||||
backoff()
|
backoff()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if args.devpath is None:
|
if args.devpath is None:
|
||||||
init.open()
|
init.open()
|
||||||
else:
|
else:
|
||||||
z = re.match('^usb-(\d+)-(\d+)$', args.devpath)
|
z = re.match(r'^usb-(\d+)-(\d+)$', args.devpath)
|
||||||
if not z:
|
if not z:
|
||||||
parser.error('Option --devpath should look like this: usb-<busnum>-<address>')
|
parser.error('Option --devpath should look like this: usb-<busnum>-<address>')
|
||||||
|
|
||||||
|
|
@ -226,9 +261,8 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
bus = dbus.SystemBus()
|
bus = dbus.SystemBus()
|
||||||
|
|
||||||
svc = Device(bus)
|
svc = Device(bus, config)
|
||||||
|
|
||||||
watcher = None
|
|
||||||
def watch_cb(name):
|
def watch_cb(name):
|
||||||
if name == '':
|
if name == '':
|
||||||
logging.debug('Manager is offline')
|
logging.debug('Manager is offline')
|
||||||
|
|
@ -237,13 +271,14 @@ if __name__ == '__main__':
|
||||||
mgr = bus.get_object(name, '/net/reactivated/Fprint/Manager')
|
mgr = bus.get_object(name, '/net/reactivated/Fprint/Manager')
|
||||||
mgr = dbus.Interface(mgr, 'net.reactivated.Fprint.Manager')
|
mgr = dbus.Interface(mgr, 'net.reactivated.Fprint.Manager')
|
||||||
mgr.RegisterDevice(svc)
|
mgr.RegisterDevice(svc)
|
||||||
|
|
||||||
watcher = bus.watch_name_owner('net.reactivated.Fprint', watch_cb)
|
watcher = bus.watch_name_owner('net.reactivated.Fprint', watch_cb)
|
||||||
|
|
||||||
# Kick off the open-fprintd if it was not started yet
|
# Kick off the open-fprintd if it was not started yet
|
||||||
bus.get_object('net.reactivated.Fprint', '/net/reactivated/Fprint/Manager')
|
bus.get_object('net.reactivated.Fprint', '/net/reactivated/Fprint/Manager')
|
||||||
|
|
||||||
def die(x):
|
def die(x):
|
||||||
logging.info('Cought signal %d. Stopping...' % x)
|
logging.info('Caught signal %d. Stopping...' % x)
|
||||||
loop.quit()
|
loop.quit()
|
||||||
|
|
||||||
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, die, signal.SIGINT)
|
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, die, signal.SIGINT)
|
||||||
|
|
@ -254,3 +289,6 @@ if __name__ == '__main__':
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
|
||||||
1
debian/control
vendored
1
debian/control
vendored
|
|
@ -15,6 +15,7 @@ Depends: ${python3:Depends},
|
||||||
${misc:Depends},
|
${misc:Depends},
|
||||||
python3-dbus,
|
python3-dbus,
|
||||||
python3-usb,
|
python3-usb,
|
||||||
|
python3-yaml,
|
||||||
dbus,
|
dbus,
|
||||||
open-fprintd (>= 0.2~),
|
open-fprintd (>= 0.2~),
|
||||||
innoextract (>= 1.6~)
|
innoextract (>= 1.6~)
|
||||||
|
|
|
||||||
1
debian/install
vendored
1
debian/install
vendored
|
|
@ -4,3 +4,4 @@ usr/lib/python*/*-packages/validitysensor/*.py
|
||||||
usr/lib/python-validity
|
usr/lib/python-validity
|
||||||
usr/share/dbus-1
|
usr/share/dbus-1
|
||||||
usr/share/python-validity
|
usr/share/python-validity
|
||||||
|
etc/python-validity
|
||||||
|
|
|
||||||
2
debian/rules
vendored
2
debian/rules
vendored
|
|
@ -12,4 +12,4 @@ override_dh_installsystemd:
|
||||||
|
|
||||||
override_dh_auto_install:
|
override_dh_auto_install:
|
||||||
python3 ./setup.py install --root=$(CURDIR)/debian/tmp --prefix=/usr --install-layout=deb
|
python3 ./setup.py install --root=$(CURDIR)/debian/tmp --prefix=/usr --install-layout=deb
|
||||||
|
install -Dm 644 etc/dbus-service.yaml $(CURDIR)/debian/tmp/etc/python-validity/dbus-service.yaml
|
||||||
|
|
|
||||||
8
etc/dbus-service.yaml
Normal file
8
etc/dbus-service.yaml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Mapping for users
|
||||||
|
# Add mappings from a user to a SID to use the same fingerprints as enrolled by
|
||||||
|
# Windows. By default, mappings will be generated from the numerical UID if not
|
||||||
|
# found here. This is fine if you are not dual booting and want to share
|
||||||
|
# mappings with Windows.
|
||||||
|
user_to_sid:
|
||||||
|
# Example:
|
||||||
|
#"username": "S-1-5-21-1234567890-1234567890-1234567890-1234"
|
||||||
3
setup.py
3
setup.py
|
|
@ -12,7 +12,8 @@ setup(name='python-validity',
|
||||||
],
|
],
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'cryptography >= 2.1.4',
|
'cryptography >= 2.1.4',
|
||||||
'pyusb >= 1.0.0'
|
'pyusb >= 1.0.0',
|
||||||
|
'pyyaml >= 3.12'
|
||||||
],
|
],
|
||||||
data_files=[
|
data_files=[
|
||||||
('share/dbus-1/system.d/', ['dbus_service/io.github.uunicorn.Fprint.conf']),
|
('share/dbus-1/system.d/', ['dbus_service/io.github.uunicorn.Fprint.conf']),
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import typing
|
||||||
|
|
||||||
from .tls import tls
|
from .tls import tls
|
||||||
from .util import assert_status
|
from .util import assert_status
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
|
|
@ -23,7 +25,7 @@ class User():
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<User: dbid=%04x identity=%s fingers=%s>' % (self.dbid, repr(self.identity), repr(self.fingers))
|
return '<User: dbid=%04x identity=%s fingers=%s>' % (self.dbid, repr(self.identity), repr(self.fingers))
|
||||||
|
|
||||||
def subtype_to_string(s):
|
def subtype_to_string(s: int):
|
||||||
if s < 0xf5 or s > 0xfe:
|
if s < 0xf5 or s > 0xfe:
|
||||||
return 'Unknown'
|
return 'Unknown'
|
||||||
|
|
||||||
|
|
@ -66,7 +68,7 @@ def parse_identity(b):
|
||||||
|
|
||||||
raise Exception('Don''t know how to handle identity type %d' % t)
|
raise Exception('Don''t know how to handle identity type %d' % t)
|
||||||
|
|
||||||
def parse_user(rsp):
|
def parse_user(rsp: bytes):
|
||||||
assert_status(rsp[:2])
|
assert_status(rsp[:2])
|
||||||
rsp=rsp[2:]
|
rsp=rsp[2:]
|
||||||
|
|
||||||
|
|
@ -88,7 +90,7 @@ def parse_user(rsp):
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
def identity_to_bytes(identity):
|
def identity_to_bytes(identity: str):
|
||||||
if isinstance(identity, SidIdentity):
|
if isinstance(identity, SidIdentity):
|
||||||
b=identity.to_bytes()
|
b=identity.to_bytes()
|
||||||
b = pack('<LL', 3, len(b)) + b
|
b = pack('<LL', 3, len(b)) + b
|
||||||
|
|
@ -152,7 +154,7 @@ class Db():
|
||||||
def get_user(self, dbid):
|
def get_user(self, dbid):
|
||||||
return parse_user(tls.cmd(pack('<BHHH', 0x4a, dbid, 0, 0)))
|
return parse_user(tls.cmd(pack('<BHHH', 0x4a, dbid, 0, 0)))
|
||||||
|
|
||||||
def lookup_user(self, identity):
|
def lookup_user(self, identity: str) -> typing.Optional[User]:
|
||||||
stg = self.get_user_storage(name='StgWindsor')
|
stg = self.get_user_storage(name='StgWindsor')
|
||||||
data = identity_to_bytes(identity)
|
data = identity_to_bytes(identity)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
|
import typing
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
import os.path
|
import os.path
|
||||||
|
|
@ -795,7 +795,7 @@ class Sensor():
|
||||||
|
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
def match_finger(self):
|
def match_finger(self) -> typing.Tuple[int, int, bytes]:
|
||||||
try:
|
try:
|
||||||
stg_id=0 # match against any storage
|
stg_id=0 # match against any storage
|
||||||
usr_id=0 # match against any user
|
usr_id=0 # match against any user
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
|
|
||||||
from struct import unpack, pack
|
from struct import unpack, pack
|
||||||
|
import typing
|
||||||
|
|
||||||
class SidIdentity():
|
class SidIdentity():
|
||||||
def __init__(self, revision, auth, subauth):
|
def __init__(self, revision: int, auth: int, subauth: typing.Sequence[int]):
|
||||||
self.revision = revision
|
self.revision = revision
|
||||||
self.auth = auth
|
self.auth = auth
|
||||||
self.subauth = subauth
|
self.subauth = subauth
|
||||||
|
|
@ -17,7 +18,7 @@ class SidIdentity():
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'S-%d-%d-%s' % (self.revision, self.auth, '-'.join(map(str, self.subauth)))
|
return 'S-%d-%d-%s' % (self.revision, self.auth, '-'.join(map(str, self.subauth)))
|
||||||
|
|
||||||
def sid_from_bytes(b):
|
def sid_from_bytes(b: bytes):
|
||||||
revision = b[0]
|
revision = b[0]
|
||||||
subcnt = b[1]
|
subcnt = b[1]
|
||||||
auth = 0
|
auth = 0
|
||||||
|
|
@ -30,7 +31,7 @@ def sid_from_bytes(b):
|
||||||
|
|
||||||
return SidIdentity(revision, auth, subauth)
|
return SidIdentity(revision, auth, subauth)
|
||||||
|
|
||||||
def sid_from_string(s):
|
def sid_from_string(s: str):
|
||||||
parts = s.split('-')
|
parts = s.split('-')
|
||||||
|
|
||||||
if parts[0] != 'S':
|
if parts[0] != 'S':
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue