mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-03 20:32:07 -04:00
greeter(auth): Enhance fingerprint/U2F auth support w/Quickshell PAM
- Split auth capability state by lock screen and greeter - Share detection between settings UI and lock runtime - Broaden greeter PAM include detection across supported distros
This commit is contained in:
@@ -524,6 +524,16 @@ func syncInTerminal(nonInteractive bool, forceAuth bool, local bool) error {
|
|||||||
return runCommandInTerminal(shellCmd)
|
return runCommandInTerminal(shellCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resolveLocalWrapperShell() (string, error) {
|
||||||
|
for _, shellName := range []string{"bash", "sh"} {
|
||||||
|
shellPath, err := exec.LookPath(shellName)
|
||||||
|
if err == nil {
|
||||||
|
return shellPath, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("could not find bash or sh in PATH for local greeter wrapper")
|
||||||
|
}
|
||||||
|
|
||||||
func syncGreeter(nonInteractive bool, forceAuth bool, local bool) error {
|
func syncGreeter(nonInteractive bool, forceAuth bool, local bool) error {
|
||||||
if !nonInteractive {
|
if !nonInteractive {
|
||||||
fmt.Println("=== DMS Greeter Theme Sync ===")
|
fmt.Println("=== DMS Greeter Theme Sync ===")
|
||||||
@@ -660,8 +670,12 @@ func syncGreeter(nonInteractive bool, forceAuth bool, local bool) error {
|
|||||||
localWrapperScript := filepath.Join(dmsPath, "Modules", "Greetd", "assets", "dms-greeter")
|
localWrapperScript := filepath.Join(dmsPath, "Modules", "Greetd", "assets", "dms-greeter")
|
||||||
restoreWrapperOverride := func() {}
|
restoreWrapperOverride := func() {}
|
||||||
if info, statErr := os.Stat(localWrapperScript); statErr == nil && !info.IsDir() {
|
if info, statErr := os.Stat(localWrapperScript); statErr == nil && !info.IsDir() {
|
||||||
|
wrapperShell, shellErr := resolveLocalWrapperShell()
|
||||||
|
if shellErr != nil {
|
||||||
|
return shellErr
|
||||||
|
}
|
||||||
previousWrapperOverride, hadWrapperOverride := os.LookupEnv("DMS_GREETER_WRAPPER_CMD")
|
previousWrapperOverride, hadWrapperOverride := os.LookupEnv("DMS_GREETER_WRAPPER_CMD")
|
||||||
wrapperCmdOverride := "/usr/bin/bash " + localWrapperScript
|
wrapperCmdOverride := wrapperShell + " " + localWrapperScript
|
||||||
_ = os.Setenv("DMS_GREETER_WRAPPER_CMD", wrapperCmdOverride)
|
_ = os.Setenv("DMS_GREETER_WRAPPER_CMD", wrapperCmdOverride)
|
||||||
restoreWrapperOverride = func() {
|
restoreWrapperOverride = func() {
|
||||||
if hadWrapperOverride {
|
if hadWrapperOverride {
|
||||||
|
|||||||
@@ -33,14 +33,23 @@ const (
|
|||||||
legacyGreeterPamU2FComment = "# DMS greeter U2F"
|
legacyGreeterPamU2FComment = "# DMS greeter U2F"
|
||||||
)
|
)
|
||||||
|
|
||||||
var includedPamAuthFiles = []string{"system-auth", "common-auth", "password-auth"}
|
// Common PAM auth stack names referenced by greetd across supported distros.
|
||||||
|
var includedPamAuthFiles = []string{
|
||||||
|
"system-auth",
|
||||||
|
"common-auth",
|
||||||
|
"password-auth",
|
||||||
|
"system-login",
|
||||||
|
"system-local-login",
|
||||||
|
"common-auth-pc",
|
||||||
|
"login",
|
||||||
|
}
|
||||||
|
|
||||||
func DetectDMSPath() (string, error) {
|
func DetectDMSPath() (string, error) {
|
||||||
return config.LocateDMSConfig()
|
return config.LocateDMSConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNixOS returns true when running on NixOS, which manages PAM configs through
|
// IsNixOS returns true when running on NixOS, which manages PAM configs through
|
||||||
// its module system. The DMS PAM managed block must not be written on NixOS.
|
// its module system. The DMS PAM managed block won't be written on NixOS.
|
||||||
func IsNixOS() bool {
|
func IsNixOS() bool {
|
||||||
_, err := os.Stat("/etc/NIXOS")
|
_, err := os.Stat("/etc/NIXOS")
|
||||||
return err == nil
|
return err == nil
|
||||||
@@ -440,8 +449,21 @@ func TryInstallGreeterPackage(logFunc func(string), sudoPassword string) bool {
|
|||||||
obsSlug := getDebianOBSSlug(osInfo)
|
obsSlug := getDebianOBSSlug(osInfo)
|
||||||
keyURL := fmt.Sprintf("https://download.opensuse.org/repositories/home:AvengeMedia:danklinux/%s/Release.key", obsSlug)
|
keyURL := fmt.Sprintf("https://download.opensuse.org/repositories/home:AvengeMedia:danklinux/%s/Release.key", obsSlug)
|
||||||
repoLine := fmt.Sprintf("deb [signed-by=/etc/apt/keyrings/danklinux.gpg] https://download.opensuse.org/repositories/home:/AvengeMedia:/danklinux/%s/ /", obsSlug)
|
repoLine := fmt.Sprintf("deb [signed-by=/etc/apt/keyrings/danklinux.gpg] https://download.opensuse.org/repositories/home:/AvengeMedia:/danklinux/%s/ /", obsSlug)
|
||||||
failHint = fmt.Sprintf("⚠ dms-greeter install failed. Add OBS repo manually:\ncurl -fsSL %s | sudo gpg --dearmor -o /etc/apt/keyrings/danklinux.gpg\necho '%s' | sudo tee /etc/apt/sources.list.d/danklinux.list\nsudo apt update && sudo apt install dms-greeter", keyURL, repoLine)
|
failHint = fmt.Sprintf("⚠ dms-greeter install failed. Add OBS repo manually:\nsudo apt-get install -y gnupg\nsudo mkdir -p /etc/apt/keyrings\ncurl -fsSL %s | sudo gpg --dearmor -o /etc/apt/keyrings/danklinux.gpg\necho '%s' | sudo tee /etc/apt/sources.list.d/danklinux.list\nsudo apt update && sudo apt-get install -y dms-greeter", keyURL, repoLine)
|
||||||
logFunc(fmt.Sprintf("Adding DankLinux OBS repository (%s)...", obsSlug))
|
logFunc(fmt.Sprintf("Adding DankLinux OBS repository (%s)...", obsSlug))
|
||||||
|
if _, err := exec.LookPath("gpg"); err != nil {
|
||||||
|
logFunc("Installing gnupg for OBS repository key import...")
|
||||||
|
installGPGCmd := exec.CommandContext(ctx, "sudo", "apt-get", "install", "-y", "gnupg")
|
||||||
|
installGPGCmd.Stdout = os.Stdout
|
||||||
|
installGPGCmd.Stderr = os.Stderr
|
||||||
|
if err := installGPGCmd.Run(); err != nil {
|
||||||
|
logFunc(fmt.Sprintf("⚠ Failed to install gnupg: %v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mkdirCmd := exec.CommandContext(ctx, "sudo", "mkdir", "-p", "/etc/apt/keyrings")
|
||||||
|
mkdirCmd.Stdout = os.Stdout
|
||||||
|
mkdirCmd.Stderr = os.Stderr
|
||||||
|
mkdirCmd.Run()
|
||||||
addKeyCmd := exec.CommandContext(ctx, "bash", "-c",
|
addKeyCmd := exec.CommandContext(ctx, "bash", "-c",
|
||||||
fmt.Sprintf(`curl -fsSL %s | sudo gpg --dearmor -o /etc/apt/keyrings/danklinux.gpg`, keyURL))
|
fmt.Sprintf(`curl -fsSL %s | sudo gpg --dearmor -o /etc/apt/keyrings/danklinux.gpg`, keyURL))
|
||||||
addKeyCmd.Stdout = os.Stdout
|
addKeyCmd.Stdout = os.Stdout
|
||||||
@@ -465,7 +487,7 @@ func TryInstallGreeterPackage(logFunc func(string), sudoPassword string) bool {
|
|||||||
exec.CommandContext(ctx, "sudo", "zypper", "refresh").Run()
|
exec.CommandContext(ctx, "sudo", "zypper", "refresh").Run()
|
||||||
installCmd = exec.CommandContext(ctx, "sudo", "zypper", "install", "-y", "dms-greeter")
|
installCmd = exec.CommandContext(ctx, "sudo", "zypper", "install", "-y", "dms-greeter")
|
||||||
case distros.FamilyUbuntu:
|
case distros.FamilyUbuntu:
|
||||||
failHint = "⚠ dms-greeter install failed. Add PPA manually: sudo add-apt-repository ppa:avengemedia/danklinux && sudo apt-get update && sudo apt-get install dms-greeter"
|
failHint = "⚠ dms-greeter install failed. Add PPA manually: sudo add-apt-repository ppa:avengemedia/danklinux && sudo apt-get update && sudo apt-get install -y dms-greeter"
|
||||||
logFunc("Enabling PPA ppa:avengemedia/danklinux...")
|
logFunc("Enabling PPA ppa:avengemedia/danklinux...")
|
||||||
ppacmd := exec.CommandContext(ctx, "sudo", "add-apt-repository", "-y", "ppa:avengemedia/danklinux")
|
ppacmd := exec.CommandContext(ctx, "sudo", "add-apt-repository", "-y", "ppa:avengemedia/danklinux")
|
||||||
ppacmd.Stdout = os.Stdout
|
ppacmd.Stdout = os.Stdout
|
||||||
@@ -834,7 +856,14 @@ func EnsureACLInstalled(logFunc func(string), sudoPassword string) error {
|
|||||||
installCmd = exec.CommandContext(ctx, "sudo", "zypper", "install", "-y", "acl")
|
installCmd = exec.CommandContext(ctx, "sudo", "zypper", "install", "-y", "acl")
|
||||||
}
|
}
|
||||||
|
|
||||||
case distros.FamilyUbuntu, distros.FamilyDebian:
|
case distros.FamilyUbuntu:
|
||||||
|
if sudoPassword != "" {
|
||||||
|
installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "apt-get install -y acl")
|
||||||
|
} else {
|
||||||
|
installCmd = exec.CommandContext(ctx, "sudo", "apt-get", "install", "-y", "acl")
|
||||||
|
}
|
||||||
|
|
||||||
|
case distros.FamilyDebian:
|
||||||
if sudoPassword != "" {
|
if sudoPassword != "" {
|
||||||
installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "apt-get install -y acl")
|
installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "apt-get install -y acl")
|
||||||
} else {
|
} else {
|
||||||
@@ -1209,9 +1238,14 @@ func pamModuleExists(module string) bool {
|
|||||||
for _, libDir := range []string{
|
for _, libDir := range []string{
|
||||||
"/usr/lib64/security",
|
"/usr/lib64/security",
|
||||||
"/usr/lib/security",
|
"/usr/lib/security",
|
||||||
|
"/lib64/security",
|
||||||
|
"/lib/security",
|
||||||
"/lib/x86_64-linux-gnu/security",
|
"/lib/x86_64-linux-gnu/security",
|
||||||
"/usr/lib/x86_64-linux-gnu/security",
|
"/usr/lib/x86_64-linux-gnu/security",
|
||||||
|
"/lib/aarch64-linux-gnu/security",
|
||||||
"/usr/lib/aarch64-linux-gnu/security",
|
"/usr/lib/aarch64-linux-gnu/security",
|
||||||
|
"/run/current-system/sw/lib64/security",
|
||||||
|
"/run/current-system/sw/lib/security",
|
||||||
} {
|
} {
|
||||||
if _, err := os.Stat(filepath.Join(libDir, module)); err == nil {
|
if _, err := os.Stat(filepath.Join(libDir, module)); err == nil {
|
||||||
return true
|
return true
|
||||||
@@ -2147,7 +2181,7 @@ func DisableConflictingDisplayManagers(sudoPassword string, logFunc func(string)
|
|||||||
switch state {
|
switch state {
|
||||||
case "enabled", "enabled-runtime", "static", "indirect", "alias":
|
case "enabled", "enabled-runtime", "static", "indirect", "alias":
|
||||||
logFunc(fmt.Sprintf("Disabling conflicting display manager: %s", dm))
|
logFunc(fmt.Sprintf("Disabling conflicting display manager: %s", dm))
|
||||||
if err := runSudoCmd(sudoPassword, "systemctl", "disable", "--now", dm); err != nil {
|
if err := runSudoCmd(sudoPassword, "systemctl", "disable", dm); err != nil {
|
||||||
logFunc(fmt.Sprintf("⚠ Warning: Failed to disable %s: %v", dm, err))
|
logFunc(fmt.Sprintf("⚠ Warning: Failed to disable %s: %v", dm, err))
|
||||||
} else {
|
} else {
|
||||||
logFunc(fmt.Sprintf("✓ Disabled %s", dm))
|
logFunc(fmt.Sprintf("✓ Disabled %s", dm))
|
||||||
|
|||||||
@@ -527,9 +527,23 @@ Singleton {
|
|||||||
property bool enableFprint: false
|
property bool enableFprint: false
|
||||||
property int maxFprintTries: 15
|
property int maxFprintTries: 15
|
||||||
property bool fprintdAvailable: false
|
property bool fprintdAvailable: false
|
||||||
|
property bool lockFingerprintCanEnable: false
|
||||||
|
property bool lockFingerprintReady: false
|
||||||
|
property string lockFingerprintReason: "probe_failed"
|
||||||
|
property bool greeterFingerprintCanEnable: false
|
||||||
|
property bool greeterFingerprintReady: false
|
||||||
|
property string greeterFingerprintReason: "probe_failed"
|
||||||
|
property string greeterFingerprintSource: "none"
|
||||||
property bool enableU2f: false
|
property bool enableU2f: false
|
||||||
property string u2fMode: "or"
|
property string u2fMode: "or"
|
||||||
property bool u2fAvailable: false
|
property bool u2fAvailable: false
|
||||||
|
property bool lockU2fCanEnable: false
|
||||||
|
property bool lockU2fReady: false
|
||||||
|
property string lockU2fReason: "probe_failed"
|
||||||
|
property bool greeterU2fCanEnable: false
|
||||||
|
property bool greeterU2fReady: false
|
||||||
|
property string greeterU2fReason: "probe_failed"
|
||||||
|
property string greeterU2fSource: "none"
|
||||||
property string lockScreenActiveMonitor: "all"
|
property string lockScreenActiveMonitor: "all"
|
||||||
property string lockScreenInactiveColor: "#000000"
|
property string lockScreenInactiveColor: "#000000"
|
||||||
property int lockScreenNotificationMode: 0
|
property int lockScreenNotificationMode: 0
|
||||||
@@ -1020,8 +1034,7 @@ Singleton {
|
|||||||
if (isGreeterMode)
|
if (isGreeterMode)
|
||||||
return;
|
return;
|
||||||
Processes.settingsRoot = root;
|
Processes.settingsRoot = root;
|
||||||
Processes.detectFprintd();
|
Processes.detectAuthCapabilities();
|
||||||
Processes.detectU2f();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
|
|||||||
@@ -10,6 +10,37 @@ Singleton {
|
|||||||
|
|
||||||
property var settingsRoot: null
|
property var settingsRoot: null
|
||||||
|
|
||||||
|
property string greetdPamText: ""
|
||||||
|
property string systemAuthPamText: ""
|
||||||
|
property string commonAuthPamText: ""
|
||||||
|
property string passwordAuthPamText: ""
|
||||||
|
property string systemLoginPamText: ""
|
||||||
|
property string systemLocalLoginPamText: ""
|
||||||
|
property string commonAuthPcPamText: ""
|
||||||
|
property string loginPamText: ""
|
||||||
|
property string dankshellU2fPamText: ""
|
||||||
|
property string u2fKeysText: ""
|
||||||
|
|
||||||
|
property string fingerprintProbeOutput: ""
|
||||||
|
property int fingerprintProbeExitCode: 0
|
||||||
|
property bool fingerprintProbeStreamFinished: false
|
||||||
|
property bool fingerprintProbeExited: false
|
||||||
|
property string fingerprintProbeState: "probe_failed"
|
||||||
|
|
||||||
|
property string pamSupportProbeOutput: ""
|
||||||
|
property bool pamSupportProbeStreamFinished: false
|
||||||
|
property bool pamSupportProbeExited: false
|
||||||
|
property int pamSupportProbeExitCode: 0
|
||||||
|
property bool pamFprintSupportDetected: false
|
||||||
|
property bool pamU2fSupportDetected: false
|
||||||
|
|
||||||
|
readonly property string homeDir: Quickshell.env("HOME") || ""
|
||||||
|
readonly property string u2fKeysPath: homeDir ? homeDir + "/.config/Yubico/u2f_keys" : ""
|
||||||
|
readonly property bool homeU2fKeysDetected: u2fKeysPath !== "" && u2fKeysWatcher.loaded && u2fKeysText.trim() !== ""
|
||||||
|
readonly property bool lockU2fCustomConfigDetected: pamModuleEnabled(dankshellU2fPamText, "pam_u2f")
|
||||||
|
readonly property bool greeterPamHasFprint: greeterPamStackHasModule("pam_fprintd")
|
||||||
|
readonly property bool greeterPamHasU2f: greeterPamStackHasModule("pam_u2f")
|
||||||
|
|
||||||
function envFlag(name) {
|
function envFlag(name) {
|
||||||
const value = (Quickshell.env(name) || "").trim().toLowerCase();
|
const value = (Quickshell.env(name) || "").trim().toLowerCase();
|
||||||
if (value === "1" || value === "true" || value === "yes" || value === "on")
|
if (value === "1" || value === "true" || value === "yes" || value === "on")
|
||||||
@@ -26,30 +57,305 @@ Singleton {
|
|||||||
qtToolsDetectionProcess.running = true;
|
qtToolsDetectionProcess.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectFprintd() {
|
function detectAuthCapabilities() {
|
||||||
if (!settingsRoot)
|
if (!settingsRoot)
|
||||||
return;
|
return;
|
||||||
if (forcedFprintAvailable !== null) {
|
|
||||||
settingsRoot.fprintdAvailable = forcedFprintAvailable;
|
if (forcedFprintAvailable === null) {
|
||||||
return;
|
fingerprintProbeOutput = "";
|
||||||
|
fingerprintProbeStreamFinished = false;
|
||||||
|
fingerprintProbeExited = false;
|
||||||
|
fingerprintProbeProcess.running = true;
|
||||||
|
} else {
|
||||||
|
fingerprintProbeState = forcedFprintAvailable ? "ready" : "probe_failed";
|
||||||
}
|
}
|
||||||
fprintdDetectionProcess.running = true;
|
|
||||||
|
if (forcedFprintAvailable === null || forcedU2fAvailable === null) {
|
||||||
|
pamFprintSupportDetected = false;
|
||||||
|
pamU2fSupportDetected = false;
|
||||||
|
pamSupportProbeOutput = "";
|
||||||
|
pamSupportProbeStreamFinished = false;
|
||||||
|
pamSupportProbeExited = false;
|
||||||
|
pamSupportDetectionProcess.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectFprintd() {
|
||||||
|
detectAuthCapabilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectU2f() {
|
function detectU2f() {
|
||||||
if (!settingsRoot)
|
detectAuthCapabilities();
|
||||||
return;
|
|
||||||
if (forcedU2fAvailable !== null) {
|
|
||||||
settingsRoot.u2fAvailable = forcedU2fAvailable;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
u2fDetectionProcess.running = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkPluginSettings() {
|
function checkPluginSettings() {
|
||||||
pluginSettingsCheckProcess.running = true;
|
pluginSettingsCheckProcess.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stripPamComment(line) {
|
||||||
|
if (!line)
|
||||||
|
return "";
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (!trimmed || trimmed.startsWith("#"))
|
||||||
|
return "";
|
||||||
|
const hashIdx = trimmed.indexOf("#");
|
||||||
|
if (hashIdx >= 0)
|
||||||
|
return trimmed.substring(0, hashIdx).trim();
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pamModuleEnabled(pamText, moduleName) {
|
||||||
|
if (!pamText || !moduleName)
|
||||||
|
return false;
|
||||||
|
const lines = pamText.split(/\r?\n/);
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = stripPamComment(lines[i]);
|
||||||
|
if (!line)
|
||||||
|
continue;
|
||||||
|
if (line.includes(moduleName))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pamTextIncludesFile(pamText, filename) {
|
||||||
|
if (!pamText || !filename)
|
||||||
|
return false;
|
||||||
|
const lines = pamText.split(/\r?\n/);
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = stripPamComment(lines[i]);
|
||||||
|
if (!line)
|
||||||
|
continue;
|
||||||
|
if (line.includes(filename) && (line.includes("include") || line.includes("substack") || line.startsWith("@include")))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function greeterPamStackHasModule(moduleName) {
|
||||||
|
if (pamModuleEnabled(greetdPamText, moduleName))
|
||||||
|
return true;
|
||||||
|
const includedPamStacks = [
|
||||||
|
["system-auth", systemAuthPamText],
|
||||||
|
["common-auth", commonAuthPamText],
|
||||||
|
["password-auth", passwordAuthPamText],
|
||||||
|
["system-login", systemLoginPamText],
|
||||||
|
["system-local-login", systemLocalLoginPamText],
|
||||||
|
["common-auth-pc", commonAuthPcPamText],
|
||||||
|
["login", loginPamText]
|
||||||
|
];
|
||||||
|
for (let i = 0; i < includedPamStacks.length; i++) {
|
||||||
|
const stack = includedPamStacks[i];
|
||||||
|
if (pamTextIncludesFile(greetdPamText, stack[0]) && pamModuleEnabled(stack[1], moduleName))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasEnrolledFingerprintOutput(output) {
|
||||||
|
const lower = (output || "").toLowerCase();
|
||||||
|
if (lower.includes("has fingers enrolled") || lower.includes("has fingerprints enrolled"))
|
||||||
|
return true;
|
||||||
|
const lines = lower.split(/\r?\n/);
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const trimmed = lines[i].trim();
|
||||||
|
if (trimmed.startsWith("finger:"))
|
||||||
|
return true;
|
||||||
|
if (trimmed.startsWith("- ") && trimmed.includes("finger"))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasMissingFingerprintEnrollmentOutput(output) {
|
||||||
|
const lower = (output || "").toLowerCase();
|
||||||
|
return lower.includes("no fingers enrolled")
|
||||||
|
|| lower.includes("no fingerprints enrolled")
|
||||||
|
|| lower.includes("no prints enrolled");
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasMissingFingerprintReaderOutput(output) {
|
||||||
|
const lower = (output || "").toLowerCase();
|
||||||
|
return lower.includes("no devices available")
|
||||||
|
|| lower.includes("no device available")
|
||||||
|
|| lower.includes("no devices found")
|
||||||
|
|| lower.includes("list_devices failed")
|
||||||
|
|| lower.includes("no device");
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseFingerprintProbe(exitCode, output) {
|
||||||
|
if (hasEnrolledFingerprintOutput(output))
|
||||||
|
return "ready";
|
||||||
|
if (hasMissingFingerprintEnrollmentOutput(output))
|
||||||
|
return "missing_enrollment";
|
||||||
|
if (hasMissingFingerprintReaderOutput(output))
|
||||||
|
return "missing_reader";
|
||||||
|
if (exitCode === 0)
|
||||||
|
return "missing_enrollment";
|
||||||
|
if (exitCode === 127 || (output || "").includes("__missing_command__"))
|
||||||
|
return "probe_failed";
|
||||||
|
return pamFprintSupportDetected ? "probe_failed" : "missing_pam_support";
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLockFingerprintCapability(canEnable, ready, reason) {
|
||||||
|
settingsRoot.lockFingerprintCanEnable = canEnable;
|
||||||
|
settingsRoot.lockFingerprintReady = ready;
|
||||||
|
settingsRoot.lockFingerprintReason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLockU2fCapability(canEnable, ready, reason) {
|
||||||
|
settingsRoot.lockU2fCanEnable = canEnable;
|
||||||
|
settingsRoot.lockU2fReady = ready;
|
||||||
|
settingsRoot.lockU2fReason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGreeterFingerprintCapability(canEnable, ready, reason, source) {
|
||||||
|
settingsRoot.greeterFingerprintCanEnable = canEnable;
|
||||||
|
settingsRoot.greeterFingerprintReady = ready;
|
||||||
|
settingsRoot.greeterFingerprintReason = reason;
|
||||||
|
settingsRoot.greeterFingerprintSource = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGreeterU2fCapability(canEnable, ready, reason, source) {
|
||||||
|
settingsRoot.greeterU2fCanEnable = canEnable;
|
||||||
|
settingsRoot.greeterU2fReady = ready;
|
||||||
|
settingsRoot.greeterU2fReason = reason;
|
||||||
|
settingsRoot.greeterU2fSource = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
function recomputeFingerprintCapabilities() {
|
||||||
|
if (forcedFprintAvailable !== null) {
|
||||||
|
const reason = forcedFprintAvailable ? "ready" : "probe_failed";
|
||||||
|
const source = forcedFprintAvailable ? "dms" : "none";
|
||||||
|
setLockFingerprintCapability(forcedFprintAvailable, forcedFprintAvailable, reason);
|
||||||
|
setGreeterFingerprintCapability(forcedFprintAvailable, forcedFprintAvailable, reason, source);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = fingerprintProbeState;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case "ready":
|
||||||
|
setLockFingerprintCapability(true, true, "ready");
|
||||||
|
break;
|
||||||
|
case "missing_enrollment":
|
||||||
|
setLockFingerprintCapability(true, false, "missing_enrollment");
|
||||||
|
break;
|
||||||
|
case "missing_reader":
|
||||||
|
setLockFingerprintCapability(false, false, "missing_reader");
|
||||||
|
break;
|
||||||
|
case "missing_pam_support":
|
||||||
|
setLockFingerprintCapability(false, false, "missing_pam_support");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setLockFingerprintCapability(false, false, "probe_failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (greeterPamHasFprint) {
|
||||||
|
switch (state) {
|
||||||
|
case "ready":
|
||||||
|
setGreeterFingerprintCapability(true, true, "configured_externally", "pam");
|
||||||
|
break;
|
||||||
|
case "missing_enrollment":
|
||||||
|
setGreeterFingerprintCapability(true, false, "missing_enrollment", "pam");
|
||||||
|
break;
|
||||||
|
case "missing_reader":
|
||||||
|
setGreeterFingerprintCapability(false, false, "missing_reader", "pam");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setGreeterFingerprintCapability(true, false, "probe_failed", "pam");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case "ready":
|
||||||
|
setGreeterFingerprintCapability(true, true, "ready", "dms");
|
||||||
|
break;
|
||||||
|
case "missing_enrollment":
|
||||||
|
setGreeterFingerprintCapability(true, false, "missing_enrollment", "dms");
|
||||||
|
break;
|
||||||
|
case "missing_reader":
|
||||||
|
setGreeterFingerprintCapability(false, false, "missing_reader", "none");
|
||||||
|
break;
|
||||||
|
case "missing_pam_support":
|
||||||
|
setGreeterFingerprintCapability(false, false, "missing_pam_support", "none");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setGreeterFingerprintCapability(false, false, "probe_failed", "none");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function recomputeU2fCapabilities() {
|
||||||
|
if (forcedU2fAvailable !== null) {
|
||||||
|
const reason = forcedU2fAvailable ? "ready" : "probe_failed";
|
||||||
|
const source = forcedU2fAvailable ? "dms" : "none";
|
||||||
|
setLockU2fCapability(forcedU2fAvailable, forcedU2fAvailable, reason);
|
||||||
|
setGreeterU2fCapability(forcedU2fAvailable, forcedU2fAvailable, reason, source);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lockReady = lockU2fCustomConfigDetected || homeU2fKeysDetected;
|
||||||
|
const lockCanEnable = lockReady || pamU2fSupportDetected;
|
||||||
|
const lockReason = lockReady ? "ready" : (lockCanEnable ? "missing_key_registration" : "missing_pam_support");
|
||||||
|
setLockU2fCapability(lockCanEnable, lockReady, lockReason);
|
||||||
|
|
||||||
|
if (greeterPamHasU2f) {
|
||||||
|
setGreeterU2fCapability(true, true, "configured_externally", "pam");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const greeterReady = homeU2fKeysDetected;
|
||||||
|
const greeterCanEnable = greeterReady || pamU2fSupportDetected;
|
||||||
|
const greeterReason = greeterReady ? "ready" : (greeterCanEnable ? "missing_key_registration" : "missing_pam_support");
|
||||||
|
setGreeterU2fCapability(greeterCanEnable, greeterReady, greeterReason, greeterCanEnable ? "dms" : "none");
|
||||||
|
}
|
||||||
|
|
||||||
|
function recomputeAuthCapabilities() {
|
||||||
|
if (!settingsRoot)
|
||||||
|
return;
|
||||||
|
recomputeFingerprintCapabilities();
|
||||||
|
recomputeU2fCapabilities();
|
||||||
|
settingsRoot.fprintdAvailable = settingsRoot.lockFingerprintReady || settingsRoot.greeterFingerprintReady;
|
||||||
|
settingsRoot.u2fAvailable = settingsRoot.lockU2fReady || settingsRoot.greeterU2fReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
function finalizeFingerprintProbe() {
|
||||||
|
if (!fingerprintProbeStreamFinished || !fingerprintProbeExited)
|
||||||
|
return;
|
||||||
|
fingerprintProbeState = parseFingerprintProbe(fingerprintProbeExitCode, fingerprintProbeOutput);
|
||||||
|
recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
|
||||||
|
function finalizePamSupportProbe() {
|
||||||
|
if (!pamSupportProbeStreamFinished || !pamSupportProbeExited)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pamFprintSupportDetected = false;
|
||||||
|
pamU2fSupportDetected = false;
|
||||||
|
|
||||||
|
const lines = (pamSupportProbeOutput || "").trim().split(/\r?\n/);
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const parts = lines[i].split(":");
|
||||||
|
if (parts.length !== 2)
|
||||||
|
continue;
|
||||||
|
if (parts[0] === "pam_fprintd.so")
|
||||||
|
pamFprintSupportDetected = parts[1] === "true";
|
||||||
|
else if (parts[0] === "pam_u2f.so")
|
||||||
|
pamU2fSupportDetected = parts[1] === "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forcedFprintAvailable === null && fingerprintProbeState === "missing_pam_support")
|
||||||
|
fingerprintProbeState = parseFingerprintProbe(fingerprintProbeExitCode, fingerprintProbeOutput);
|
||||||
|
|
||||||
|
recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
|
||||||
property var qtToolsDetectionProcess: Process {
|
property var qtToolsDetectionProcess: Process {
|
||||||
command: ["sh", "-c", "echo -n 'qt5ct:'; command -v qt5ct >/dev/null && echo 'true' || echo 'false'; echo -n 'qt6ct:'; command -v qt6ct >/dev/null && echo 'true' || echo 'false'; echo -n 'gtk:'; (command -v gsettings >/dev/null || command -v dconf >/dev/null) && echo 'true' || echo 'false'"]
|
command: ["sh", "-c", "echo -n 'qt5ct:'; command -v qt5ct >/dev/null && echo 'true' || echo 'false'; echo -n 'qt6ct:'; command -v qt6ct >/dev/null && echo 'true' || echo 'false'; echo -n 'gtk:'; (command -v gsettings >/dev/null || command -v dconf >/dev/null) && echo 'true' || echo 'false'"]
|
||||||
running: false
|
running: false
|
||||||
@@ -59,15 +365,15 @@ Singleton {
|
|||||||
if (!settingsRoot)
|
if (!settingsRoot)
|
||||||
return;
|
return;
|
||||||
if (text && text.trim()) {
|
if (text && text.trim()) {
|
||||||
var lines = text.trim().split('\n');
|
const lines = text.trim().split("\n");
|
||||||
for (var i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
var line = lines[i];
|
const line = lines[i];
|
||||||
if (line.startsWith('qt5ct:')) {
|
if (line.startsWith("qt5ct:")) {
|
||||||
settingsRoot.qt5ctAvailable = line.split(':')[1] === 'true';
|
settingsRoot.qt5ctAvailable = line.split(":")[1] === "true";
|
||||||
} else if (line.startsWith('qt6ct:')) {
|
} else if (line.startsWith("qt6ct:")) {
|
||||||
settingsRoot.qt6ctAvailable = line.split(':')[1] === 'true';
|
settingsRoot.qt6ctAvailable = line.split(":")[1] === "true";
|
||||||
} else if (line.startsWith('gtk:')) {
|
} else if (line.startsWith("gtk:")) {
|
||||||
settingsRoot.gtkAvailable = line.split(':')[1] === 'true';
|
settingsRoot.gtkAvailable = line.split(":")[1] === "true";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,23 +381,181 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property var fprintdDetectionProcess: Process {
|
property var fingerprintProbeProcess: Process {
|
||||||
command: ["sh", "-c", "command -v fprintd-list >/dev/null 2>&1 && fprintd-list \"${USER:-$(id -un)}\" >/dev/null 2>&1"]
|
command: ["sh", "-c", "if command -v fprintd-list >/dev/null 2>&1; then fprintd-list \"${USER:-$(id -un)}\" 2>&1; else printf '__missing_command__\\n'; exit 127; fi"]
|
||||||
running: false
|
running: false
|
||||||
|
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
root.fingerprintProbeOutput = text || "";
|
||||||
|
root.fingerprintProbeStreamFinished = true;
|
||||||
|
root.finalizeFingerprintProbe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onExited: function (exitCode) {
|
onExited: function (exitCode) {
|
||||||
if (!settingsRoot)
|
root.fingerprintProbeExitCode = exitCode;
|
||||||
return;
|
root.fingerprintProbeExited = true;
|
||||||
settingsRoot.fprintdAvailable = (exitCode === 0);
|
root.finalizeFingerprintProbe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property var u2fDetectionProcess: Process {
|
property var pamSupportDetectionProcess: Process {
|
||||||
command: ["sh", "-c", "(test -f /usr/lib/security/pam_u2f.so || test -f /usr/lib64/security/pam_u2f.so) && (test -f /etc/pam.d/dankshell-u2f || test -f \"$HOME/.config/Yubico/u2f_keys\")"]
|
command: ["sh", "-c", "for module in pam_fprintd.so pam_u2f.so; do found=false; for dir in /usr/lib64/security /usr/lib/security /lib/security /lib/x86_64-linux-gnu/security /usr/lib/x86_64-linux-gnu/security /usr/lib/aarch64-linux-gnu/security /run/current-system/sw/lib/security; do if [ -f \"$dir/$module\" ]; then found=true; break; fi; done; printf '%s:%s\\n' \"$module\" \"$found\"; done"]
|
||||||
running: false
|
running: false
|
||||||
|
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
root.pamSupportProbeOutput = text || "";
|
||||||
|
root.pamSupportProbeStreamFinished = true;
|
||||||
|
root.finalizePamSupportProbe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onExited: function (exitCode) {
|
onExited: function (exitCode) {
|
||||||
if (!settingsRoot)
|
root.pamSupportProbeExitCode = exitCode;
|
||||||
return;
|
root.pamSupportProbeExited = true;
|
||||||
settingsRoot.u2fAvailable = (exitCode === 0);
|
root.finalizePamSupportProbe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: greetdPamWatcher
|
||||||
|
path: "/etc/pam.d/greetd"
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: {
|
||||||
|
root.greetdPamText = text();
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
root.greetdPamText = "";
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: systemAuthPamWatcher
|
||||||
|
path: "/etc/pam.d/system-auth"
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: {
|
||||||
|
root.systemAuthPamText = text();
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
root.systemAuthPamText = "";
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: commonAuthPamWatcher
|
||||||
|
path: "/etc/pam.d/common-auth"
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: {
|
||||||
|
root.commonAuthPamText = text();
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
root.commonAuthPamText = "";
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: passwordAuthPamWatcher
|
||||||
|
path: "/etc/pam.d/password-auth"
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: {
|
||||||
|
root.passwordAuthPamText = text();
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
root.passwordAuthPamText = "";
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: systemLoginPamWatcher
|
||||||
|
path: "/etc/pam.d/system-login"
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: {
|
||||||
|
root.systemLoginPamText = text();
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
root.systemLoginPamText = "";
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: systemLocalLoginPamWatcher
|
||||||
|
path: "/etc/pam.d/system-local-login"
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: {
|
||||||
|
root.systemLocalLoginPamText = text();
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
root.systemLocalLoginPamText = "";
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: commonAuthPcPamWatcher
|
||||||
|
path: "/etc/pam.d/common-auth-pc"
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: {
|
||||||
|
root.commonAuthPcPamText = text();
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
root.commonAuthPcPamText = "";
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: loginPamWatcher
|
||||||
|
path: "/etc/pam.d/login"
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: {
|
||||||
|
root.loginPamText = text();
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
root.loginPamText = "";
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: dankshellU2fPamWatcher
|
||||||
|
path: "/etc/pam.d/dankshell-u2f"
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: {
|
||||||
|
root.dankshellU2fPamText = text();
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
root.dankshellU2fPamText = "";
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: u2fKeysWatcher
|
||||||
|
path: root.u2fKeysPath
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: {
|
||||||
|
root.u2fKeysText = text();
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
root.u2fKeysText = "";
|
||||||
|
root.recomputeAuthCapabilities();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -341,9 +341,23 @@ var SPEC = {
|
|||||||
enableFprint: { def: false },
|
enableFprint: { def: false },
|
||||||
maxFprintTries: { def: 15 },
|
maxFprintTries: { def: 15 },
|
||||||
fprintdAvailable: { def: false, persist: false },
|
fprintdAvailable: { def: false, persist: false },
|
||||||
|
lockFingerprintCanEnable: { def: false, persist: false },
|
||||||
|
lockFingerprintReady: { def: false, persist: false },
|
||||||
|
lockFingerprintReason: { def: "probe_failed", persist: false },
|
||||||
|
greeterFingerprintCanEnable: { def: false, persist: false },
|
||||||
|
greeterFingerprintReady: { def: false, persist: false },
|
||||||
|
greeterFingerprintReason: { def: "probe_failed", persist: false },
|
||||||
|
greeterFingerprintSource: { def: "none", persist: false },
|
||||||
enableU2f: { def: false },
|
enableU2f: { def: false },
|
||||||
u2fMode: { def: "or" },
|
u2fMode: { def: "or" },
|
||||||
u2fAvailable: { def: false, persist: false },
|
u2fAvailable: { def: false, persist: false },
|
||||||
|
lockU2fCanEnable: { def: false, persist: false },
|
||||||
|
lockU2fReady: { def: false, persist: false },
|
||||||
|
lockU2fReason: { def: "probe_failed", persist: false },
|
||||||
|
greeterU2fCanEnable: { def: false, persist: false },
|
||||||
|
greeterU2fReady: { def: false, persist: false },
|
||||||
|
greeterU2fReason: { def: "probe_failed", persist: false },
|
||||||
|
greeterU2fSource: { def: "none", persist: false },
|
||||||
lockScreenActiveMonitor: { def: "all" },
|
lockScreenActiveMonitor: { def: "all" },
|
||||||
lockScreenInactiveColor: { def: "#000000" },
|
lockScreenInactiveColor: { def: "#000000" },
|
||||||
lockScreenNotificationMode: { def: 0 },
|
lockScreenNotificationMode: { def: 0 },
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ Item {
|
|||||||
property bool passwordSubmitRequested: false
|
property bool passwordSubmitRequested: false
|
||||||
property bool cancelingExternalAuthForPassword: false
|
property bool cancelingExternalAuthForPassword: false
|
||||||
property int defaultAuthTimeoutMs: 10000
|
property int defaultAuthTimeoutMs: 10000
|
||||||
property int externalAuthTimeoutMs: 36000
|
property int externalAuthTimeoutMs: 30000
|
||||||
property int memoryFlushDelayMs: 120
|
property int memoryFlushDelayMs: 120
|
||||||
property string pendingLaunchCommand: ""
|
property string pendingLaunchCommand: ""
|
||||||
property var pendingLaunchEnv: []
|
property var pendingLaunchEnv: []
|
||||||
@@ -47,13 +47,17 @@ Item {
|
|||||||
property string systemAuthPamText: ""
|
property string systemAuthPamText: ""
|
||||||
property string commonAuthPamText: ""
|
property string commonAuthPamText: ""
|
||||||
property string passwordAuthPamText: ""
|
property string passwordAuthPamText: ""
|
||||||
|
property string systemLoginPamText: ""
|
||||||
|
property string systemLocalLoginPamText: ""
|
||||||
|
property string commonAuthPcPamText: ""
|
||||||
|
property string loginPamText: ""
|
||||||
property string faillockConfigText: ""
|
property string faillockConfigText: ""
|
||||||
property bool greeterWallpaperOverrideExists: false
|
property bool greeterWallpaperOverrideExists: false
|
||||||
property string externalAuthAutoStartedForUser: ""
|
property string externalAuthAutoStartedForUser: ""
|
||||||
property int passwordSessionTransitionRetryCount: 0
|
property int passwordSessionTransitionRetryCount: 0
|
||||||
property int maxPasswordSessionTransitionRetries: 2
|
property int maxPasswordSessionTransitionRetries: 2
|
||||||
readonly property bool greeterPamHasFprint: pamModuleEnabled(greetdPamText, "pam_fprintd") || (greetdPamText.includes("system-auth") && pamModuleEnabled(systemAuthPamText, "pam_fprintd")) || (greetdPamText.includes("common-auth") && pamModuleEnabled(commonAuthPamText, "pam_fprintd")) || (greetdPamText.includes("password-auth") && pamModuleEnabled(passwordAuthPamText, "pam_fprintd"))
|
readonly property bool greeterPamHasFprint: greeterPamStackHasModule("pam_fprintd")
|
||||||
readonly property bool greeterPamHasU2f: pamModuleEnabled(greetdPamText, "pam_u2f") || (greetdPamText.includes("system-auth") && pamModuleEnabled(systemAuthPamText, "pam_u2f")) || (greetdPamText.includes("common-auth") && pamModuleEnabled(commonAuthPamText, "pam_u2f")) || (greetdPamText.includes("password-auth") && pamModuleEnabled(passwordAuthPamText, "pam_u2f"))
|
readonly property bool greeterPamHasU2f: greeterPamStackHasModule("pam_u2f")
|
||||||
readonly property bool greeterExternalAuthAvailable: (greeterPamHasFprint && GreetdSettings.greeterEnableFprint) || (greeterPamHasU2f && GreetdSettings.greeterEnableU2f)
|
readonly property bool greeterExternalAuthAvailable: (greeterPamHasFprint && GreetdSettings.greeterEnableFprint) || (greeterPamHasU2f && GreetdSettings.greeterEnableU2f)
|
||||||
readonly property bool greeterPamHasExternalAuth: greeterPamHasFprint || greeterPamHasU2f
|
readonly property bool greeterPamHasExternalAuth: greeterPamHasFprint || greeterPamHasU2f
|
||||||
|
|
||||||
@@ -95,6 +99,40 @@ Item {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pamTextIncludesFile(pamText, filename) {
|
||||||
|
if (!pamText || !filename)
|
||||||
|
return false;
|
||||||
|
const lines = pamText.split(/\r?\n/);
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = stripPamComment(lines[i]);
|
||||||
|
if (!line)
|
||||||
|
continue;
|
||||||
|
if (line.includes(filename) && (line.includes("include") || line.includes("substack") || line.startsWith("@include")))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function greeterPamStackHasModule(moduleName) {
|
||||||
|
if (pamModuleEnabled(greetdPamText, moduleName))
|
||||||
|
return true;
|
||||||
|
const includedPamStacks = [
|
||||||
|
["system-auth", systemAuthPamText],
|
||||||
|
["common-auth", commonAuthPamText],
|
||||||
|
["password-auth", passwordAuthPamText],
|
||||||
|
["system-login", systemLoginPamText],
|
||||||
|
["system-local-login", systemLocalLoginPamText],
|
||||||
|
["common-auth-pc", commonAuthPcPamText],
|
||||||
|
["login", loginPamText]
|
||||||
|
];
|
||||||
|
for (let i = 0; i < includedPamStacks.length; i++) {
|
||||||
|
const stack = includedPamStacks[i];
|
||||||
|
if (pamTextIncludesFile(greetdPamText, stack[0]) && pamModuleEnabled(stack[1], moduleName))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function usesPamLockoutPolicy(pamText) {
|
function usesPamLockoutPolicy(pamText) {
|
||||||
if (!pamText)
|
if (!pamText)
|
||||||
return false;
|
return false;
|
||||||
@@ -148,7 +186,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function refreshPasswordAttemptPolicyHint() {
|
function refreshPasswordAttemptPolicyHint() {
|
||||||
const pamSources = [greetdPamText, systemAuthPamText, commonAuthPamText, passwordAuthPamText];
|
const pamSources = [greetdPamText, systemAuthPamText, commonAuthPamText, passwordAuthPamText, systemLoginPamText, systemLocalLoginPamText, commonAuthPcPamText, loginPamText];
|
||||||
let lockoutConfigured = false;
|
let lockoutConfigured = false;
|
||||||
let denyFromPam = -1;
|
let denyFromPam = -1;
|
||||||
for (let i = 0; i < pamSources.length; i++) {
|
for (let i = 0; i < pamSources.length; i++) {
|
||||||
@@ -271,6 +309,7 @@ Item {
|
|||||||
onLoaded: {
|
onLoaded: {
|
||||||
root.systemAuthPamText = text();
|
root.systemAuthPamText = text();
|
||||||
root.refreshPasswordAttemptPolicyHint();
|
root.refreshPasswordAttemptPolicyHint();
|
||||||
|
root.maybeAutoStartExternalAuth();
|
||||||
}
|
}
|
||||||
onLoadFailed: {
|
onLoadFailed: {
|
||||||
root.systemAuthPamText = "";
|
root.systemAuthPamText = "";
|
||||||
@@ -285,6 +324,7 @@ Item {
|
|||||||
onLoaded: {
|
onLoaded: {
|
||||||
root.commonAuthPamText = text();
|
root.commonAuthPamText = text();
|
||||||
root.refreshPasswordAttemptPolicyHint();
|
root.refreshPasswordAttemptPolicyHint();
|
||||||
|
root.maybeAutoStartExternalAuth();
|
||||||
}
|
}
|
||||||
onLoadFailed: {
|
onLoadFailed: {
|
||||||
root.commonAuthPamText = "";
|
root.commonAuthPamText = "";
|
||||||
@@ -299,6 +339,7 @@ Item {
|
|||||||
onLoaded: {
|
onLoaded: {
|
||||||
root.passwordAuthPamText = text();
|
root.passwordAuthPamText = text();
|
||||||
root.refreshPasswordAttemptPolicyHint();
|
root.refreshPasswordAttemptPolicyHint();
|
||||||
|
root.maybeAutoStartExternalAuth();
|
||||||
}
|
}
|
||||||
onLoadFailed: {
|
onLoadFailed: {
|
||||||
root.passwordAuthPamText = "";
|
root.passwordAuthPamText = "";
|
||||||
@@ -306,6 +347,66 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: systemLoginPamWatcher
|
||||||
|
path: "/etc/pam.d/system-login"
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: {
|
||||||
|
root.systemLoginPamText = text();
|
||||||
|
root.refreshPasswordAttemptPolicyHint();
|
||||||
|
root.maybeAutoStartExternalAuth();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
root.systemLoginPamText = "";
|
||||||
|
root.refreshPasswordAttemptPolicyHint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: systemLocalLoginPamWatcher
|
||||||
|
path: "/etc/pam.d/system-local-login"
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: {
|
||||||
|
root.systemLocalLoginPamText = text();
|
||||||
|
root.refreshPasswordAttemptPolicyHint();
|
||||||
|
root.maybeAutoStartExternalAuth();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
root.systemLocalLoginPamText = "";
|
||||||
|
root.refreshPasswordAttemptPolicyHint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: commonAuthPcPamWatcher
|
||||||
|
path: "/etc/pam.d/common-auth-pc"
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: {
|
||||||
|
root.commonAuthPcPamText = text();
|
||||||
|
root.refreshPasswordAttemptPolicyHint();
|
||||||
|
root.maybeAutoStartExternalAuth();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
root.commonAuthPcPamText = "";
|
||||||
|
root.refreshPasswordAttemptPolicyHint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: loginPamWatcher
|
||||||
|
path: "/etc/pam.d/login"
|
||||||
|
printErrors: false
|
||||||
|
onLoaded: {
|
||||||
|
root.loginPamText = text();
|
||||||
|
root.refreshPasswordAttemptPolicyHint();
|
||||||
|
root.maybeAutoStartExternalAuth();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
root.loginPamText = "";
|
||||||
|
root.refreshPasswordAttemptPolicyHint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FileView {
|
FileView {
|
||||||
id: faillockConfigWatcher
|
id: faillockConfigWatcher
|
||||||
path: "/etc/security/faillock.conf"
|
path: "/etc/security/faillock.conf"
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ Scope {
|
|||||||
PamContext {
|
PamContext {
|
||||||
id: fprint
|
id: fprint
|
||||||
|
|
||||||
property bool available
|
property bool available: SettingsData.lockFingerprintReady
|
||||||
property int tries
|
property int tries
|
||||||
property int errorTries
|
property int errorTries
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ Scope {
|
|||||||
PamContext {
|
PamContext {
|
||||||
id: u2f
|
id: u2f
|
||||||
|
|
||||||
property bool available
|
property bool available: SettingsData.lockU2fReady
|
||||||
|
|
||||||
function checkAvail(): void {
|
function checkAvail(): void {
|
||||||
if (!available || !SettingsData.enableU2f || !root.lockSecured) {
|
if (!available || !SettingsData.enableU2f || !root.lockSecured) {
|
||||||
@@ -288,26 +288,6 @@ Scope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
|
||||||
id: availProc
|
|
||||||
|
|
||||||
command: ["sh", "-c", "fprintd-list \"${USER:-$(id -un)}\""]
|
|
||||||
onExited: code => {
|
|
||||||
fprint.available = code === 0;
|
|
||||||
fprint.checkAvail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: u2fAvailProc
|
|
||||||
|
|
||||||
command: ["sh", "-c", "(test -f /usr/lib/security/pam_u2f.so || test -f /usr/lib64/security/pam_u2f.so) && (test -f /etc/pam.d/dankshell-u2f || test -f \"$HOME/.config/Yubico/u2f_keys\")"]
|
|
||||||
onExited: code => {
|
|
||||||
u2f.available = code === 0;
|
|
||||||
u2f.checkAvail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: errorRetry
|
id: errorRetry
|
||||||
|
|
||||||
@@ -371,14 +351,15 @@ Scope {
|
|||||||
|
|
||||||
onLockSecuredChanged: {
|
onLockSecuredChanged: {
|
||||||
if (lockSecured) {
|
if (lockSecured) {
|
||||||
availProc.running = true;
|
SettingsData.refreshAuthAvailability();
|
||||||
u2fAvailProc.running = true;
|
|
||||||
root.state = "";
|
root.state = "";
|
||||||
root.fprintState = "";
|
root.fprintState = "";
|
||||||
root.u2fState = "";
|
root.u2fState = "";
|
||||||
root.u2fPending = false;
|
root.u2fPending = false;
|
||||||
root.lockMessage = "";
|
root.lockMessage = "";
|
||||||
root.resetAuthFlows();
|
root.resetAuthFlows();
|
||||||
|
fprint.checkAvail();
|
||||||
|
u2f.checkAvail();
|
||||||
} else {
|
} else {
|
||||||
root.resetAuthFlows();
|
root.resetAuthFlows();
|
||||||
}
|
}
|
||||||
@@ -391,10 +372,18 @@ Scope {
|
|||||||
fprint.checkAvail();
|
fprint.checkAvail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onLockFingerprintReadyChanged(): void {
|
||||||
|
fprint.checkAvail();
|
||||||
|
}
|
||||||
|
|
||||||
function onEnableU2fChanged(): void {
|
function onEnableU2fChanged(): void {
|
||||||
u2f.checkAvail();
|
u2f.checkAvail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onLockU2fReadyChanged(): void {
|
||||||
|
u2f.checkAvail();
|
||||||
|
}
|
||||||
|
|
||||||
function onU2fModeChanged(): void {
|
function onU2fModeChanged(): void {
|
||||||
if (root.lockSecured) {
|
if (root.lockSecured) {
|
||||||
u2f.abort();
|
u2f.abort();
|
||||||
|
|||||||
@@ -14,8 +14,63 @@ import qs.Modules.Settings.Widgets
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property bool greeterFprintToggleAvailable: SettingsData.fprintdAvailable || SettingsData.greeterEnableFprint
|
readonly property bool greeterFprintToggleAvailable: SettingsData.greeterFingerprintCanEnable || SettingsData.greeterEnableFprint
|
||||||
readonly property bool greeterU2fToggleAvailable: SettingsData.u2fAvailable || SettingsData.greeterEnableU2f
|
readonly property bool greeterU2fToggleAvailable: SettingsData.greeterU2fCanEnable || SettingsData.greeterEnableU2f
|
||||||
|
|
||||||
|
function greeterFingerprintDescription() {
|
||||||
|
const source = SettingsData.greeterFingerprintSource;
|
||||||
|
const reason = SettingsData.greeterFingerprintReason;
|
||||||
|
|
||||||
|
if (source === "pam") {
|
||||||
|
switch (reason) {
|
||||||
|
case "configured_externally":
|
||||||
|
return SettingsData.greeterEnableFprint ? I18n.tr("Enabled. PAM already provides fingerprint auth.") : I18n.tr("PAM already provides fingerprint auth. Enable this to show it at login.");
|
||||||
|
case "missing_enrollment":
|
||||||
|
return SettingsData.greeterEnableFprint ? I18n.tr("Enabled. PAM provides fingerprint auth, but no prints are enrolled yet.") : I18n.tr("PAM provides fingerprint auth, but no prints are enrolled yet.");
|
||||||
|
case "missing_reader":
|
||||||
|
return I18n.tr("PAM provides fingerprint auth, but no reader was detected.");
|
||||||
|
default:
|
||||||
|
return I18n.tr("PAM provides fingerprint auth, but availability could not be confirmed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reason) {
|
||||||
|
case "ready":
|
||||||
|
return SettingsData.greeterEnableFprint ? I18n.tr("Run Sync to apply. Fingerprint-only login may not unlock GNOME Keyring.") : I18n.tr("Only affects DMS-managed PAM. If greetd already includes pam_fprintd, fingerprint stays enabled.");
|
||||||
|
case "missing_enrollment":
|
||||||
|
if (SettingsData.greeterEnableFprint)
|
||||||
|
return I18n.tr("Enabled, but no prints are enrolled yet. Enroll fingerprints and run Sync.");
|
||||||
|
return I18n.tr("Fingerprint reader detected, but no prints are enrolled yet. You can enable this now and run Sync later.");
|
||||||
|
case "missing_reader":
|
||||||
|
return SettingsData.greeterEnableFprint ? I18n.tr("Enabled, but no fingerprint reader was detected.") : I18n.tr("No fingerprint reader detected.");
|
||||||
|
case "missing_pam_support":
|
||||||
|
return I18n.tr("Not available — install fprintd and pam_fprintd, or configure greetd PAM.");
|
||||||
|
default:
|
||||||
|
return SettingsData.greeterEnableFprint ? I18n.tr("Enabled, but fingerprint availability could not be confirmed.") : I18n.tr("Fingerprint availability could not be confirmed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function greeterU2fDescription() {
|
||||||
|
const source = SettingsData.greeterU2fSource;
|
||||||
|
const reason = SettingsData.greeterU2fReason;
|
||||||
|
|
||||||
|
if (source === "pam") {
|
||||||
|
return SettingsData.greeterEnableU2f ? I18n.tr("Enabled. PAM already provides security-key auth.") : I18n.tr("PAM already provides security-key auth. Enable this to show it at login.");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reason) {
|
||||||
|
case "ready":
|
||||||
|
return SettingsData.greeterEnableU2f ? I18n.tr("Run Sync to apply.") : I18n.tr("Available.");
|
||||||
|
case "missing_key_registration":
|
||||||
|
if (SettingsData.greeterEnableU2f)
|
||||||
|
return I18n.tr("Enabled, but no registered security key was found yet. Register a key and run Sync.");
|
||||||
|
return I18n.tr("Security-key support was detected, but no registered key was found yet. You can enable this now and register one later.");
|
||||||
|
case "missing_pam_support":
|
||||||
|
return I18n.tr("Not available — install or configure pam_u2f, or configure greetd PAM.");
|
||||||
|
default:
|
||||||
|
return SettingsData.greeterEnableU2f ? I18n.tr("Enabled, but security-key availability could not be confirmed.") : I18n.tr("Security-key availability could not be confirmed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function refreshAuthDetection() {
|
function refreshAuthDetection() {
|
||||||
SettingsData.refreshAuthAvailability();
|
SettingsData.refreshAuthAvailability();
|
||||||
@@ -481,15 +536,8 @@ Item {
|
|||||||
settingKey: "greeterEnableFprint"
|
settingKey: "greeterEnableFprint"
|
||||||
tags: ["greeter", "fingerprint", "fprintd", "login", "auth"]
|
tags: ["greeter", "fingerprint", "fprintd", "login", "auth"]
|
||||||
text: I18n.tr("Enable fingerprint at login")
|
text: I18n.tr("Enable fingerprint at login")
|
||||||
description: {
|
description: root.greeterFingerprintDescription()
|
||||||
if (!SettingsData.fprintdAvailable) {
|
descriptionColor: (SettingsData.greeterFingerprintReason === "ready" || SettingsData.greeterFingerprintReason === "configured_externally") ? Theme.surfaceVariantText : Theme.warning
|
||||||
if (SettingsData.greeterEnableFprint)
|
|
||||||
return I18n.tr("Enabled in settings, but fingerprint availability could not yet be confirmed. Re-open after enrolling fingerprints or reconnecting the reader.");
|
|
||||||
return I18n.tr("Not available — install fprintd and enroll fingerprints.");
|
|
||||||
}
|
|
||||||
return SettingsData.greeterEnableFprint ? I18n.tr("Run Sync to apply. Fingerprint-only login may not unlock GNOME Keyring.") : I18n.tr("Only off for DMS-managed PAM lines. If greetd includes system-auth/common-auth/password-auth with pam_fprintd, fingerprint still stays enabled.");
|
|
||||||
}
|
|
||||||
descriptionColor: SettingsData.fprintdAvailable ? Theme.surfaceVariantText : Theme.warning
|
|
||||||
checked: SettingsData.greeterEnableFprint
|
checked: SettingsData.greeterEnableFprint
|
||||||
enabled: root.greeterFprintToggleAvailable
|
enabled: root.greeterFprintToggleAvailable
|
||||||
onToggled: checked => SettingsData.set("greeterEnableFprint", checked)
|
onToggled: checked => SettingsData.set("greeterEnableFprint", checked)
|
||||||
@@ -499,15 +547,8 @@ Item {
|
|||||||
settingKey: "greeterEnableU2f"
|
settingKey: "greeterEnableU2f"
|
||||||
tags: ["greeter", "u2f", "security", "key", "login", "auth"]
|
tags: ["greeter", "u2f", "security", "key", "login", "auth"]
|
||||||
text: I18n.tr("Enable security key at login")
|
text: I18n.tr("Enable security key at login")
|
||||||
description: {
|
description: root.greeterU2fDescription()
|
||||||
if (!SettingsData.u2fAvailable) {
|
descriptionColor: (SettingsData.greeterU2fReason === "ready" || SettingsData.greeterU2fReason === "configured_externally") ? Theme.surfaceVariantText : Theme.warning
|
||||||
if (SettingsData.greeterEnableU2f)
|
|
||||||
return I18n.tr("Enabled in settings, but security key availability could not yet be confirmed. Re-open after enrolling keys or updating pam_u2f.");
|
|
||||||
return I18n.tr("Not available — install pam_u2f and enroll keys.");
|
|
||||||
}
|
|
||||||
return SettingsData.greeterEnableU2f ? I18n.tr("Run Sync to apply.") : I18n.tr("Disabled.");
|
|
||||||
}
|
|
||||||
descriptionColor: SettingsData.u2fAvailable ? Theme.surfaceVariantText : Theme.warning
|
|
||||||
checked: SettingsData.greeterEnableU2f
|
checked: SettingsData.greeterEnableU2f
|
||||||
enabled: root.greeterU2fToggleAvailable
|
enabled: root.greeterU2fToggleAvailable
|
||||||
onToggled: checked => SettingsData.set("greeterEnableU2f", checked)
|
onToggled: checked => SettingsData.set("greeterEnableU2f", checked)
|
||||||
|
|||||||
@@ -9,8 +9,40 @@ import qs.Modules.Settings.Widgets
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property bool lockFprintToggleAvailable: SettingsData.fprintdAvailable || SettingsData.enableFprint
|
readonly property bool lockFprintToggleAvailable: SettingsData.lockFingerprintCanEnable || SettingsData.enableFprint
|
||||||
readonly property bool lockU2fToggleAvailable: SettingsData.u2fAvailable || SettingsData.enableU2f
|
readonly property bool lockU2fToggleAvailable: SettingsData.lockU2fCanEnable || SettingsData.enableU2f
|
||||||
|
|
||||||
|
function lockFingerprintDescription() {
|
||||||
|
switch (SettingsData.lockFingerprintReason) {
|
||||||
|
case "ready":
|
||||||
|
return I18n.tr("Use fingerprint authentication for the lock screen.");
|
||||||
|
case "missing_enrollment":
|
||||||
|
if (SettingsData.enableFprint)
|
||||||
|
return I18n.tr("Enabled, but no prints are enrolled yet. Enroll fingerprints to use it.");
|
||||||
|
return I18n.tr("Fingerprint reader detected, but no prints are enrolled yet. You can enable this now and enroll later.");
|
||||||
|
case "missing_reader":
|
||||||
|
return SettingsData.enableFprint ? I18n.tr("Enabled, but no fingerprint reader was detected.") : I18n.tr("No fingerprint reader detected.");
|
||||||
|
case "missing_pam_support":
|
||||||
|
return I18n.tr("Not available — install fprintd and pam_fprintd.");
|
||||||
|
default:
|
||||||
|
return SettingsData.enableFprint ? I18n.tr("Enabled, but fingerprint availability could not be confirmed.") : I18n.tr("Fingerprint availability could not be confirmed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function lockU2fDescription() {
|
||||||
|
switch (SettingsData.lockU2fReason) {
|
||||||
|
case "ready":
|
||||||
|
return I18n.tr("Use a security key for lock screen authentication.", "lock screen U2F security key setting");
|
||||||
|
case "missing_key_registration":
|
||||||
|
if (SettingsData.enableU2f)
|
||||||
|
return I18n.tr("Enabled, but no registered security key was found yet. Register a key or update your U2F config.");
|
||||||
|
return I18n.tr("Security-key support was detected, but no registered key was found yet. You can enable this now and register one later.");
|
||||||
|
case "missing_pam_support":
|
||||||
|
return I18n.tr("Not available — install or configure pam_u2f.");
|
||||||
|
default:
|
||||||
|
return SettingsData.enableU2f ? I18n.tr("Enabled, but security-key availability could not be confirmed.") : I18n.tr("Security-key availability could not be confirmed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function refreshAuthDetection() {
|
function refreshAuthDetection() {
|
||||||
SettingsData.refreshAuthAvailability();
|
SettingsData.refreshAuthAvailability();
|
||||||
@@ -185,14 +217,8 @@ Item {
|
|||||||
settingKey: "enableFprint"
|
settingKey: "enableFprint"
|
||||||
tags: ["lock", "screen", "fingerprint", "authentication", "biometric", "fprint"]
|
tags: ["lock", "screen", "fingerprint", "authentication", "biometric", "fprint"]
|
||||||
text: I18n.tr("Enable fingerprint authentication")
|
text: I18n.tr("Enable fingerprint authentication")
|
||||||
description: {
|
description: root.lockFingerprintDescription()
|
||||||
if (SettingsData.fprintdAvailable)
|
descriptionColor: SettingsData.lockFingerprintReason === "ready" ? Theme.surfaceVariantText : Theme.warning
|
||||||
return I18n.tr("Use fingerprint reader for lock screen authentication (requires enrolled fingerprints)");
|
|
||||||
if (SettingsData.enableFprint)
|
|
||||||
return I18n.tr("Enabled in settings, but fingerprint availability could not yet be confirmed. Re-open after enrolling fingerprints or reconnecting the reader.");
|
|
||||||
return I18n.tr("Not available — install fprintd and enroll fingerprints.");
|
|
||||||
}
|
|
||||||
descriptionColor: SettingsData.fprintdAvailable ? Theme.surfaceVariantText : Theme.warning
|
|
||||||
checked: SettingsData.enableFprint
|
checked: SettingsData.enableFprint
|
||||||
enabled: root.lockFprintToggleAvailable
|
enabled: root.lockFprintToggleAvailable
|
||||||
onToggled: checked => SettingsData.set("enableFprint", checked)
|
onToggled: checked => SettingsData.set("enableFprint", checked)
|
||||||
@@ -202,14 +228,8 @@ Item {
|
|||||||
settingKey: "enableU2f"
|
settingKey: "enableU2f"
|
||||||
tags: ["lock", "screen", "u2f", "yubikey", "security", "key", "fido", "authentication", "hardware"]
|
tags: ["lock", "screen", "u2f", "yubikey", "security", "key", "fido", "authentication", "hardware"]
|
||||||
text: I18n.tr("Enable security key authentication", "Enable FIDO2/U2F hardware security key for lock screen")
|
text: I18n.tr("Enable security key authentication", "Enable FIDO2/U2F hardware security key for lock screen")
|
||||||
description: {
|
description: root.lockU2fDescription()
|
||||||
if (SettingsData.u2fAvailable)
|
descriptionColor: SettingsData.lockU2fReason === "ready" ? Theme.surfaceVariantText : Theme.warning
|
||||||
return I18n.tr("Use a FIDO2/U2F security key (e.g. YubiKey) for lock screen authentication (requires enrolled keys)", "lock screen U2F security key setting");
|
|
||||||
if (SettingsData.enableU2f)
|
|
||||||
return I18n.tr("Enabled in settings, but security key availability could not yet be confirmed. Re-open after enrolling keys or updating pam_u2f.");
|
|
||||||
return I18n.tr("Not available — install pam_u2f and enroll keys.");
|
|
||||||
}
|
|
||||||
descriptionColor: SettingsData.u2fAvailable ? Theme.surfaceVariantText : Theme.warning
|
|
||||||
checked: SettingsData.enableU2f
|
checked: SettingsData.enableU2f
|
||||||
enabled: root.lockU2fToggleAvailable
|
enabled: root.lockU2fToggleAvailable
|
||||||
onToggled: checked => SettingsData.set("enableU2f", checked)
|
onToggled: checked => SettingsData.set("enableU2f", checked)
|
||||||
|
|||||||
Reference in New Issue
Block a user