#!/usr/bin/env python3 # File: fprintd_tk_lib.py # Location: https://bgstack15.cgit/fprintd-tk # Author: bgstack15 # Startdate: 2024-09-22-1 15:33 # SPDX-License-Identifier: GPL-3.0-only # Title: Backend for fprintd_tk that uses fprintd binaries # Purpose: In case this gets rewritten to use dbus directly or something # Project: fprintd-tk # History: # Usage: # Reference: # Improve: # Dependencies: # dep-devuan: fprintd # /usr/bin/fprintd-* # Documentation: # README.md import os, subprocess, re _user = os.getenv("USER") fre = re.compile ("^.* - #[0-9]+: ([^ ]+)$") prevent_success_messages = [ "no-match", "failed to claim", "not enrolled for user", "enroll-duplicate" ] def get_enrolled_fingers(user = None, verbose = False): """ Returns list of full strings of fingers that are enrolled for the listed user. """ enrolled_fingers = [] if user is None: user = _user if verbose: print(f"DEBUG (get_enrolled_fingers): user {user}") proc = subprocess.Popen( ["fprintd-list",user], stdout = subprocess.PIPE, universal_newlines = True # or maybe text=True ) while True: line = proc.stdout.readline() if verbose: print(f"DEBUG (get_enrolled_fingers): line {line}") if not line: break if fre.match(line): enrolled_fingers.append(fre.match(line).groups()[0].strip()) elif re.match("^.*No devices available.*", line): return [] elif re.match("^.*has no fingers enrolled.*", line): return ["none"] else: pass return enrolled_fingers def fprintd_action(action, finger, status_function = None, user = None, verbose = False): if user is None: user = _user if action == "enroll": command = ["fprintd-enroll","-f",finger,user] elif action == "verify": command = ["fprintd-verify","-f",finger,user] elif action == "delete": command = ["fprintd-delete",user] else: if status_function: status_function(f"Invalid action {action}") return False if verbose: print(f"DEBUG (fprintd_action): command {command}") proc = subprocess.Popen( command, stdout = subprocess.PIPE, universal_newlines = True ) old_line = "INITIAL_LINE" dupe_count = 0 while True: line = proc.stdout.readline() if not line: break display_line = line if line == old_line: dupe_count = dupe_count + 1 display_line = line.strip() + str(f" (x{dupe_count})") else: dupe_count = 1 old_line = line if status_function: status_function(display_line) # If you want to react to any lines. #if re.match("^.*verify-match (done).*",line): # proc.kill() # return "verify-match" # so the process has ended, now what? proc.kill() if status_function: if display_line: display = True for i in prevent_success_messages: if i in display_line: display = False break if display: status_function(f"Succeeded! {display_line}") def check_setusername_permission(status_function = None, verbose = False): """ This permission depends on a rule like this in /etc/polkit-1/rules.d/80-fprintd.rules: polkit.addRule(function(action, subject) { if ( ( action.id.match("net.reactivated.fprint.device.setusername") ) && subject.active && subject.isInGroup("admins")) { polkit.log("action=" + action); polkit.log("subject=" + subject); return polkit.Result.YES; } }); We need to check for the ability to run `fprintd-list root` and if it does not print "not authorized", we can allow the advanced actions. """ proc = subprocess.Popen( ["fprintd-list","root"], stdout = subprocess.PIPE, universal_newlines = True # or maybe text=True ) has_setusername = True while True: line = proc.stdout.readline() if verbose: print(f"DEBUG (check_setusername_permission): line {line}") if not line: break if re.match(".*Not Authorized.*",line): if verbose: print(f"DEBUG (check_setusername_permission): setting has_setusername to false...") has_setusername = False if status_function: status_function(f"Have advanced permissions: {has_setusername}") return has_setusername