aboutsummaryrefslogtreecommitdiff
path: root/fprintd_tk_lib.py
blob: 18470cb9fd2347e776d9caa04fd0d98d36c31a2a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/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:
#    /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
bgstack15