aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md30
-rw-r--r--aux/80-fprintd.rules29
-rwxr-xr-xfprintd_tk.py54
-rw-r--r--fprintd_tk_lib.py66
4 files changed, 162 insertions, 17 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e60cbfb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,30 @@
+# README for fprintd_tk
+This project is a desktop application that makes it easy for a user to control fingerprint enrollments.
+
+## Upstream
+The upstream project is hosted at <https://bgstack15.ddns.net/cgit/fprintd_tk>
+
+## Alternatives
+
+* Use `fprintd-enroll` on cli.
+* Use whatever probably-GNOME software is demonstrated in <https://wiki.debian.org/SecurityManagement/fingerprint%20authentication>
+
+## Reason for existence
+I couldn't find a window-manager friendly gui for managing fingerprints.
+
+## Using
+Run the program.
+
+## Dependencies
+
+* [python-tkstackrpms](https://bgstack15.ddns.net/cgit/python3-tkstackrpms/)
+* python3-tk
+* python3-pil
+
+The application really needs the user to have password-less auth permission in dbus/polkit to the fprintd actions. You will want a file like [80-fprintd.rules](aux/80-fprintd.rules)
+
+## Building
+
+## References
+
+* Icons adapted from [Numix Circle icon theme](https://github.com/numixproject/numix-icon-theme-circle) under GPL 3.0
diff --git a/aux/80-fprintd.rules b/aux/80-fprintd.rules
new file mode 100644
index 0000000..1da22cf
--- /dev/null
+++ b/aux/80-fprintd.rules
@@ -0,0 +1,29 @@
+/*
+.. File: 80-fprintd.rules
+.. Startdate: 2023-01-12-5 15:59
+.. History:
+.. 2024-09-24 updated for current correctness required
+.. Purpose: replaced fprintd.pkla for bgconf 0.1.34
+.. Origin: placed by fingerprint-scanner.sh
+*/
+polkit.addRule(function(action, subject) {
+ if (
+ (
+ action.id.match("net.reactivated.fprint.device.enroll") ||
+ action.id.match("net.reactivated.fprint.device.verify")
+ ) && subject.active) {
+ polkit.log("action=" + action);
+ polkit.log("subject=" + subject);
+ return polkit.Result.YES;
+ }
+});
+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;
+ }
+});
diff --git a/fprintd_tk.py b/fprintd_tk.py
index 492f2eb..f7442d8 100755
--- a/fprintd_tk.py
+++ b/fprintd_tk.py
@@ -1,11 +1,23 @@
#!/usr/bin/env python3
+# File: fprintd_tk.py
+# Location: https://bgstack15.cgit/fprintd-tk
+# Author: bgstack15
# Startdate: 2024-09-22-1 14:26
+# SPDX-License-Identifier: GPL-3.0-only
+# Title: Gui for fprintd
# Purpose: tkinter desktop gui app for management fingerprint enrollments
-# Dependencies:
-# python3-tkstackrpms, python3-tk, python3-pil
-# References:
+# Project: fprintd-tk
+# History:
+# Usage:
+# Run from window manager application menu
+# Reference:
# stackrpms_tk.py
# Improve:
+# Dependencies:
+# dep-devuan: python3-tkstackrpms, python3-tk, python3-pil
+# fprintd_tk_lib.py
+# Documentation:
+# README.md
import tkinter as tk, os, tkinter.simpledialog, sys, threading, time
import tkstackrpms as stk
@@ -19,7 +31,7 @@ SPDX-License-Identifier: GPL-3.0-only
Icons adapted from Numix-Icon-Theme-Circle (GPL 3)
"""
-# configurable by admin
+# configurable by admin or installation
img_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"images")
# These will be used a lot in the program.
@@ -39,13 +51,15 @@ class App(tk.Frame):
self.advanced.trace_add("write",self.load_data_into_form)
self.current_username = os.getenv("USER")
self.username = tk.StringVar(value=self.current_username)
+ self.verbose = tk.BooleanVar(value=False)
self.master.title("Gui for fprintd")
imgicon = stk.get_scaled_icon("fingerprint-gui",24,"default", "","apps")
self.master.tk.call("wm","iconphoto",self.master._w,imgicon)
menu = tk.Menu(self.master)
menu_file = tk.Menu(menu,tearoff=0)
- menu_file.add_checkbutton(label="Advanced features", variable=self.advanced, underline=0)
+ menu_file.add_checkbutton(label="Advanced features", variable=self.advanced, underline=0, state="disabled")
+ menu_file.add_checkbutton(label="Verbose", variable=self.verbose, underline=0)
menu_file.add_command(label="Delete...", command=self.func_delete, underline=0)
menu_file.add_separator()
menu_file.add_command(label="Exit", command=self.func_exit, underline=1)
@@ -106,6 +120,15 @@ class App(tk.Frame):
# status bar
stk.StatusBar(self.master,var=self.statustext)
+ # check if user has setusername polkit-1 permission, which lets him control fingerprint enrollments for other users.
+ temp1 = lib.check_setusername_permission(self.func_update_status)
+ if self.verbose.get():
+ print(f"DEBUG (init): has set_username: {temp1}")
+ if temp1:
+ #self.chk_advanced.configure(state="enabled")
+ #menu_file.child[0].child[0].configure(state="enabled")
+ menu_file.entryconfigure(0,state="normal")
+ #print(menu_file.children)
# and now, load data into form for the first time
self.load_data_into_form()
@@ -128,10 +151,12 @@ class App(tk.Frame):
# show advanced toolbar
used_user = self.get_used_user()
if advanced:
- print(f"DEBUG: showing advanced toolbar")
+ if self.verbose.get():
+ print(f"DEBUG: showing advanced toolbar")
self.frm_advanced.grid(row=0,column=0,columnspan=100)
else:
- print(f"DEBUG: hiding advanced toolbar")
+ if self.verbose.get():
+ print(f"DEBUG: hiding advanced toolbar")
self.frm_advanced.grid_forget()
# update enrolled fingers icons
try:
@@ -143,9 +168,11 @@ class App(tk.Frame):
if temp1:
self.enrolled_fingers = temp1
else:
- print(f"DEBUG (load_data_into_form): having to skip empty response from get_enrolled_fingers")
+ if self.verbose.get():
+ print(f"DEBUG (load_data_into_form): having to skip empty response from get_enrolled_fingers")
self.func_update_status(f"Unable to read enrolled fingers for user {used_user}.", reload = False)
- print(f"DEBUG (load_data_into_form): got user {used_user} enrolled fingers {self.enrolled_fingers}")
+ if self.verbose.get():
+ print(f"DEBUG (load_data_into_form): got user {used_user} enrolled fingers {self.enrolled_fingers}")
for i in self.fingers:
if str(i.cget('text')) in self.enrolled_fingers:
i.config(image=self.img_enrolled)
@@ -163,7 +190,8 @@ class App(tk.Frame):
self.master.quit()
def func_finger_button(self, finger):
- print(f"DEBUG: func_finger_button finger {finger}, action {self.action.get()}")
+ if self.verbose.get():
+ print(f"DEBUG: func_finger_button finger {finger}, action {self.action.get()}")
action = self.action.get()
used_user = self.get_used_user()
# position in array is same as the value coming from radio button for actions.
@@ -188,13 +216,15 @@ class App(tk.Frame):
def func_update_status(self, msg, reload = True):
msg = msg.strip()
- print(f"DEBUG (func_update_status): msg {msg}",file=sys.stderr)
+ if self.verbose.get():
+ print(f"DEBUG (func_update_status): msg {msg}",file=sys.stderr)
self.statustext.set(msg)
try:
this_finger = [i for i in self.fingers if i.cget("text") == self.last_fingerbutton_used.get()][0]
except Exception as e:
this_finger = None
- print(f"DEBUG (func_update_status): while evaluating last-used finger, got {e}")
+ if self.verbose.get():
+ print(f"DEBUG (func_update_status): while evaluating last-used finger, got {e}")
# flash these red
failure_messages = [
"enroll-duplicate",
diff --git a/fprintd_tk_lib.py b/fprintd_tk_lib.py
index bc97448..18470cb 100644
--- a/fprintd_tk_lib.py
+++ b/fprintd_tk_lib.py
@@ -1,9 +1,20 @@
#!/usr/bin/env python3
+# File: fprintd_tk_lib.py
+# Location: https://bgstack15.cgit/fprintd-tk
+# Author: bgstack15
# Startdate: 2024-09-22-1 15:33
-# Purpose: backend for fprintd_tk that uses fprintd-* binaries, in case I ever rewrite this to use dbus directly?
+# 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-*
-# Improve:
+# Documentation:
+# README.md
import os, subprocess, re
@@ -17,11 +28,14 @@ prevent_success_messages = [
]
def get_enrolled_fingers(user = None, verbose = False):
- # return list of full strings of fingers that are enrolled.
+ """
+ Returns list of full strings of fingers that are enrolled for the listed user.
+ """
enrolled_fingers = []
if user is None:
user = _user
- print(f"DEBUG (get_enrolled_fingers): user {user}")
+ if verbose:
+ print(f"DEBUG (get_enrolled_fingers): user {user}")
proc = subprocess.Popen(
["fprintd-list",user],
stdout = subprocess.PIPE,
@@ -29,12 +43,16 @@ def get_enrolled_fingers(user = None, verbose = False):
)
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
@@ -52,7 +70,8 @@ def fprintd_action(action, finger, status_function = None, user = None, verbose
if status_function:
status_function(f"Invalid action {action}")
return False
- print(f"DEBUG (fprintd_action): command {command}")
+ if verbose:
+ print(f"DEBUG (fprintd_action): command {command}")
proc = subprocess.Popen(
command,
stdout = subprocess.PIPE,
@@ -88,3 +107,40 @@ def fprintd_action(action, finger, status_function = None, user = None, verbose
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