battery_boost.authenticate

Prompt for and validate sudo credentials for Battery Boost.

Provides a GUI dialog to request the user's password and cache sudo credentials for subsequent TLP commands. Exits the program if authentication fails.

authenticate

Prompt user for sudo password and validate.

Runs sudo -v to cache credentials for subsequent commands. Retries up to three times before exiting the program.

Exits program if authentication fails.

Source code in src/battery_boost/authenticate.py
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
def authenticate(_parent: App) -> None:
    """Prompt user for sudo password and validate.

    Runs `sudo -v` to cache credentials for subsequent commands.
    Retries up to three times before exiting the program.

    Exits program if authentication fails.
    """
    max_tries = 3
    for attempt in range(max_tries):
        _password = simpledialog.askstring(
            "Authenticate",
            "Authentication Required to run Battery Boost.\n\nEnter your password:",
            show="*"
        )

        if _password is None:
            _parent.quit_app("Cancelled.")

        # Don't strip() - whitespaces are unusual but valid password characters.
        if not _password:
            if attempt < max_tries - 1:
                messagebox.showerror("Error", "Password required.")
                continue
            break

        try:
            subprocess.run(['sudo', '-S', '-v'],
                           input=_password + '\n',
                           text=True,
                           capture_output=True,
                           timeout=20,  # Unlikely, but better than hanging.
                           check=True)
            _password = None  # Overwrite password immediately.
            return
        except FileNotFoundError:
            _parent.quit_on_error("sudo not found on this system.")
        except subprocess.TimeoutExpired:
            _parent.quit_on_error("Authentication process timed out.",
                                  "Fatal Error")
        except subprocess.CalledProcessError:
            # Only realistic failure remaining is "wrong password".
            if attempt < max_tries - 1:
                messagebox.showerror("Error", "Incorrect password.")
        except Exception as exc:  # pylint: disable=broad-exception-caught
            # Defensively catch any unexpected errors and quit.
            _parent.quit_on_error(f"Unexpected Error {exc}",
                                  "Fatal Error")

        finally:
            _password = None  # Ensure always cleared.

    # Failed every attempt.
    message = f"Authentication failed {max_tries} times.\n\nClick OK to Quit."
    _parent.quit_on_error(message)