mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-03 20:32:07 -04:00
refactor(greeter): Update auth flows and add configurable opts
- Finally fix debug info logs before dms greeter loads - prevent greeter/lockscreen auth stalls with timeout recovery and unlock-state sync
This commit is contained in:
@@ -294,6 +294,8 @@ Singleton {
|
||||
property string centeringMode: "index"
|
||||
property string clockDateFormat: ""
|
||||
property string lockDateFormat: ""
|
||||
property bool greeterRememberLastSession: true
|
||||
property bool greeterRememberLastUser: true
|
||||
property int mediaSize: 1
|
||||
|
||||
property string appLauncherViewMode: "list"
|
||||
|
||||
@@ -154,6 +154,8 @@ var SPEC = {
|
||||
centeringMode: { def: "index" },
|
||||
clockDateFormat: { def: "" },
|
||||
lockDateFormat: { def: "" },
|
||||
greeterRememberLastSession: { def: true },
|
||||
greeterRememberLastUser: { def: true },
|
||||
mediaSize: { def: 1 },
|
||||
|
||||
appLauncherViewMode: { def: "list" },
|
||||
|
||||
20
quickshell/Modules/Greetd/GreetdEnv.js
Normal file
20
quickshell/Modules/Greetd/GreetdEnv.js
Normal file
@@ -0,0 +1,20 @@
|
||||
.pragma library
|
||||
|
||||
function readBoolOverride(envReader, names, fallbackValue) {
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
const name = names[i];
|
||||
const raw = envReader(name);
|
||||
if (raw === undefined || raw === null || raw === "")
|
||||
continue;
|
||||
|
||||
const normalized = String(raw).trim().toLowerCase();
|
||||
if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on")
|
||||
return true;
|
||||
if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off")
|
||||
return false;
|
||||
|
||||
console.warn("Invalid boolean override for", name + ":", raw, "- trying next override/fallback");
|
||||
}
|
||||
|
||||
return fallbackValue;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import "GreetdEnv.js" as GreetdEnv
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
@@ -11,6 +12,8 @@ Singleton {
|
||||
readonly property string greetCfgDir: Quickshell.env("DMS_GREET_CFG_DIR") || "/etc/greetd/.dms"
|
||||
readonly property string sessionConfigPath: greetCfgDir + "/session.json"
|
||||
readonly property string memoryFile: greetCfgDir + "/memory.json"
|
||||
readonly property bool rememberLastSession: GreetdEnv.readBoolOverride(Quickshell.env, ["DMS_GREET_REMEMBER_LAST_SESSION", "DMS_SAVE_SESSION"], true)
|
||||
readonly property bool rememberLastUser: GreetdEnv.readBoolOverride(Quickshell.env, ["DMS_GREET_REMEMBER_LAST_USER", "DMS_SAVE_USERNAME"], true)
|
||||
|
||||
property string lastSessionId: ""
|
||||
property string lastSuccessfulUser: ""
|
||||
@@ -49,26 +52,44 @@ Singleton {
|
||||
if (!content || !content.trim())
|
||||
return;
|
||||
const memory = JSON.parse(content);
|
||||
lastSessionId = memory.lastSessionId || "";
|
||||
lastSuccessfulUser = memory.lastSuccessfulUser || "";
|
||||
lastSessionId = rememberLastSession ? (memory.lastSessionId || "") : "";
|
||||
lastSuccessfulUser = rememberLastUser ? (memory.lastSuccessfulUser || "") : "";
|
||||
if (!rememberLastSession || !rememberLastUser)
|
||||
saveMemory();
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse greetd memory:", e);
|
||||
}
|
||||
}
|
||||
|
||||
function saveMemory() {
|
||||
memoryFileView.setText(JSON.stringify({
|
||||
"lastSessionId": lastSessionId,
|
||||
"lastSuccessfulUser": lastSuccessfulUser
|
||||
}, null, 2));
|
||||
let memory = {};
|
||||
if (rememberLastSession && lastSessionId)
|
||||
memory.lastSessionId = lastSessionId;
|
||||
if (rememberLastUser && lastSuccessfulUser)
|
||||
memory.lastSuccessfulUser = lastSuccessfulUser;
|
||||
memoryFileView.setText(JSON.stringify(memory, null, 2));
|
||||
}
|
||||
|
||||
function setLastSessionId(id) {
|
||||
if (!rememberLastSession) {
|
||||
if (lastSessionId !== "") {
|
||||
lastSessionId = "";
|
||||
saveMemory();
|
||||
}
|
||||
return;
|
||||
}
|
||||
lastSessionId = id || "";
|
||||
saveMemory();
|
||||
}
|
||||
|
||||
function setLastSuccessfulUser(username) {
|
||||
if (!rememberLastUser) {
|
||||
if (lastSuccessfulUser !== "") {
|
||||
lastSuccessfulUser = "";
|
||||
saveMemory();
|
||||
}
|
||||
return;
|
||||
}
|
||||
lastSuccessfulUser = username || "";
|
||||
saveMemory();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import "GreetdEnv.js" as GreetdEnv
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
@@ -41,6 +42,8 @@ Singleton {
|
||||
property string lockDateFormat: ""
|
||||
property bool lockScreenShowPowerActions: true
|
||||
property bool lockScreenShowProfileImage: true
|
||||
property bool rememberLastSession: true
|
||||
property bool rememberLastUser: true
|
||||
property bool powerActionConfirm: true
|
||||
property real powerActionHoldDuration: 0.5
|
||||
property var powerMenuActions: ["reboot", "logout", "poweroff", "lock", "suspend", "restart"]
|
||||
@@ -52,53 +55,73 @@ Singleton {
|
||||
|
||||
function parseSettings(content) {
|
||||
try {
|
||||
let settings = {};
|
||||
if (content && content.trim()) {
|
||||
const settings = JSON.parse(content);
|
||||
currentThemeName = settings.currentThemeName !== undefined ? settings.currentThemeName : "purple";
|
||||
customThemeFile = settings.customThemeFile !== undefined ? settings.customThemeFile : "";
|
||||
matugenScheme = settings.matugenScheme !== undefined ? settings.matugenScheme : "scheme-tonal-spot";
|
||||
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true;
|
||||
showSeconds = settings.showSeconds !== undefined ? settings.showSeconds : false;
|
||||
padHours12Hour = settings.padHours12Hour !== undefined ? settings.padHours12Hour : false;
|
||||
useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false;
|
||||
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false;
|
||||
weatherLocation = settings.weatherLocation !== undefined ? settings.weatherLocation : "New York, NY";
|
||||
weatherCoordinates = settings.weatherCoordinates !== undefined ? settings.weatherCoordinates : "40.7128,-74.0060";
|
||||
useAutoLocation = settings.useAutoLocation !== undefined ? settings.useAutoLocation : false;
|
||||
weatherEnabled = settings.weatherEnabled !== undefined ? settings.weatherEnabled : true;
|
||||
iconTheme = settings.iconTheme !== undefined ? settings.iconTheme : "System Default";
|
||||
useOSLogo = settings.useOSLogo !== undefined ? settings.useOSLogo : false;
|
||||
osLogoColorOverride = settings.osLogoColorOverride !== undefined ? settings.osLogoColorOverride : "";
|
||||
osLogoBrightness = settings.osLogoBrightness !== undefined ? settings.osLogoBrightness : 0.5;
|
||||
osLogoContrast = settings.osLogoContrast !== undefined ? settings.osLogoContrast : 1;
|
||||
fontFamily = settings.fontFamily !== undefined ? settings.fontFamily : Theme.defaultFontFamily;
|
||||
monoFontFamily = settings.monoFontFamily !== undefined ? settings.monoFontFamily : Theme.defaultMonoFontFamily;
|
||||
fontWeight = settings.fontWeight !== undefined ? settings.fontWeight : Font.Normal;
|
||||
fontScale = settings.fontScale !== undefined ? settings.fontScale : 1.0;
|
||||
cornerRadius = settings.cornerRadius !== undefined ? settings.cornerRadius : 12;
|
||||
widgetBackgroundColor = settings.widgetBackgroundColor !== undefined ? settings.widgetBackgroundColor : "sch";
|
||||
lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : "";
|
||||
lockScreenShowPowerActions = settings.lockScreenShowPowerActions !== undefined ? settings.lockScreenShowPowerActions : true;
|
||||
lockScreenShowProfileImage = settings.lockScreenShowProfileImage !== undefined ? settings.lockScreenShowProfileImage : true;
|
||||
powerActionConfirm = settings.powerActionConfirm !== undefined ? settings.powerActionConfirm : true;
|
||||
powerActionHoldDuration = settings.powerActionHoldDuration !== undefined ? settings.powerActionHoldDuration : 0.5;
|
||||
powerMenuActions = settings.powerMenuActions !== undefined ? settings.powerMenuActions : ["reboot", "logout", "poweroff", "lock", "suspend", "restart"];
|
||||
powerMenuDefaultAction = settings.powerMenuDefaultAction !== undefined ? settings.powerMenuDefaultAction : "logout";
|
||||
powerMenuGridLayout = settings.powerMenuGridLayout !== undefined ? settings.powerMenuGridLayout : false;
|
||||
screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({});
|
||||
animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : 2;
|
||||
wallpaperFillMode = settings.wallpaperFillMode !== undefined ? settings.wallpaperFillMode : "Fill";
|
||||
settingsLoaded = true;
|
||||
settings = JSON.parse(content);
|
||||
}
|
||||
|
||||
if (typeof Theme !== "undefined") {
|
||||
if (currentThemeName === "custom" && customThemeFile) {
|
||||
Theme.loadCustomThemeFromFile(customThemeFile);
|
||||
}
|
||||
Theme.applyGreeterTheme(currentThemeName);
|
||||
const envRememberLastSession = GreetdEnv.readBoolOverride(Quickshell.env, ["DMS_GREET_REMEMBER_LAST_SESSION", "DMS_SAVE_SESSION"], undefined);
|
||||
const envRememberLastUser = GreetdEnv.readBoolOverride(Quickshell.env, ["DMS_GREET_REMEMBER_LAST_USER", "DMS_SAVE_USERNAME"], undefined);
|
||||
|
||||
currentThemeName = settings.currentThemeName !== undefined ? settings.currentThemeName : "purple";
|
||||
customThemeFile = settings.customThemeFile !== undefined ? settings.customThemeFile : "";
|
||||
matugenScheme = settings.matugenScheme !== undefined ? settings.matugenScheme : "scheme-tonal-spot";
|
||||
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true;
|
||||
showSeconds = settings.showSeconds !== undefined ? settings.showSeconds : false;
|
||||
padHours12Hour = settings.padHours12Hour !== undefined ? settings.padHours12Hour : false;
|
||||
useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false;
|
||||
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false;
|
||||
weatherLocation = settings.weatherLocation !== undefined ? settings.weatherLocation : "New York, NY";
|
||||
weatherCoordinates = settings.weatherCoordinates !== undefined ? settings.weatherCoordinates : "40.7128,-74.0060";
|
||||
useAutoLocation = settings.useAutoLocation !== undefined ? settings.useAutoLocation : false;
|
||||
weatherEnabled = settings.weatherEnabled !== undefined ? settings.weatherEnabled : true;
|
||||
iconTheme = settings.iconTheme !== undefined ? settings.iconTheme : "System Default";
|
||||
useOSLogo = settings.useOSLogo !== undefined ? settings.useOSLogo : false;
|
||||
osLogoColorOverride = settings.osLogoColorOverride !== undefined ? settings.osLogoColorOverride : "";
|
||||
osLogoBrightness = settings.osLogoBrightness !== undefined ? settings.osLogoBrightness : 0.5;
|
||||
osLogoContrast = settings.osLogoContrast !== undefined ? settings.osLogoContrast : 1;
|
||||
fontFamily = settings.fontFamily !== undefined ? settings.fontFamily : Theme.defaultFontFamily;
|
||||
monoFontFamily = settings.monoFontFamily !== undefined ? settings.monoFontFamily : Theme.defaultMonoFontFamily;
|
||||
fontWeight = settings.fontWeight !== undefined ? settings.fontWeight : Font.Normal;
|
||||
fontScale = settings.fontScale !== undefined ? settings.fontScale : 1.0;
|
||||
cornerRadius = settings.cornerRadius !== undefined ? settings.cornerRadius : 12;
|
||||
widgetBackgroundColor = settings.widgetBackgroundColor !== undefined ? settings.widgetBackgroundColor : "sch";
|
||||
lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : "";
|
||||
lockScreenShowPowerActions = settings.lockScreenShowPowerActions !== undefined ? settings.lockScreenShowPowerActions : true;
|
||||
lockScreenShowProfileImage = settings.lockScreenShowProfileImage !== undefined ? settings.lockScreenShowProfileImage : true;
|
||||
if (envRememberLastSession !== undefined) {
|
||||
rememberLastSession = envRememberLastSession;
|
||||
} else {
|
||||
rememberLastSession = settings.greeterRememberLastSession !== undefined
|
||||
? settings.greeterRememberLastSession
|
||||
: settings.rememberLastSession !== undefined ? settings.rememberLastSession : true;
|
||||
}
|
||||
if (envRememberLastUser !== undefined) {
|
||||
rememberLastUser = envRememberLastUser;
|
||||
} else {
|
||||
rememberLastUser = settings.greeterRememberLastUser !== undefined
|
||||
? settings.greeterRememberLastUser
|
||||
: settings.rememberLastUser !== undefined ? settings.rememberLastUser : true;
|
||||
}
|
||||
powerActionConfirm = settings.powerActionConfirm !== undefined ? settings.powerActionConfirm : true;
|
||||
powerActionHoldDuration = settings.powerActionHoldDuration !== undefined ? settings.powerActionHoldDuration : 0.5;
|
||||
powerMenuActions = settings.powerMenuActions !== undefined ? settings.powerMenuActions : ["reboot", "logout", "poweroff", "lock", "suspend", "restart"];
|
||||
powerMenuDefaultAction = settings.powerMenuDefaultAction !== undefined ? settings.powerMenuDefaultAction : "logout";
|
||||
powerMenuGridLayout = settings.powerMenuGridLayout !== undefined ? settings.powerMenuGridLayout : false;
|
||||
screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({});
|
||||
animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : 2;
|
||||
wallpaperFillMode = settings.wallpaperFillMode !== undefined ? settings.wallpaperFillMode : "Fill";
|
||||
|
||||
if (typeof Theme !== "undefined") {
|
||||
if (currentThemeName === "custom" && customThemeFile) {
|
||||
Theme.loadCustomThemeFromFile(customThemeFile);
|
||||
}
|
||||
Theme.applyGreeterTheme(currentThemeName);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse greetd settings:", e);
|
||||
} finally {
|
||||
settingsLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,5 +156,9 @@ Singleton {
|
||||
onLoaded: {
|
||||
parseSettings(settingsFile.text());
|
||||
}
|
||||
onLoadFailed: error => {
|
||||
console.warn("Failed to load greetd settings:", error);
|
||||
root.parseSettings("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,17 @@ Item {
|
||||
signal launchRequested
|
||||
|
||||
property bool weatherInitialized: false
|
||||
property bool awaitingExternalAuth: false
|
||||
property int defaultAuthTimeoutMs: 12000
|
||||
property int externalAuthTimeoutMs: 45000
|
||||
property int passwordFailureCount: 0
|
||||
property int passwordAttemptLimitHint: 0
|
||||
property string authFeedbackMessage: ""
|
||||
property string greetdPamText: ""
|
||||
property string systemAuthPamText: ""
|
||||
property string commonAuthPamText: ""
|
||||
property string passwordAuthPamText: ""
|
||||
property string faillockConfigText: ""
|
||||
|
||||
function initWeatherService() {
|
||||
if (weatherInitialized)
|
||||
@@ -44,16 +55,238 @@ Item {
|
||||
WeatherService.forceRefresh();
|
||||
}
|
||||
|
||||
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 usesPamLockoutPolicy(pamText) {
|
||||
if (!pamText)
|
||||
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("pam_faillock.so") || line.includes("pam_tally2.so") || line.includes("pam_tally.so"))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function parsePamLineDenyValue(pamText) {
|
||||
if (!pamText)
|
||||
return -1;
|
||||
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("pam_faillock.so") && !line.includes("pam_tally2.so") && !line.includes("pam_tally.so"))
|
||||
continue;
|
||||
const denyMatch = line.match(/\bdeny\s*=\s*(\d+)\b/i);
|
||||
if (!denyMatch)
|
||||
continue;
|
||||
const parsed = parseInt(denyMatch[1], 10);
|
||||
if (!isNaN(parsed))
|
||||
return parsed;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function parseFaillockDenyValue(configText) {
|
||||
if (!configText)
|
||||
return -1;
|
||||
const lines = configText.split(/\r?\n/);
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = stripPamComment(lines[i]);
|
||||
if (!line)
|
||||
continue;
|
||||
const denyMatch = line.match(/^deny\s*=\s*(\d+)\s*$/i);
|
||||
if (!denyMatch)
|
||||
continue;
|
||||
const parsed = parseInt(denyMatch[1], 10);
|
||||
if (!isNaN(parsed))
|
||||
return parsed;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function refreshPasswordAttemptPolicyHint() {
|
||||
const pamSources = [greetdPamText, systemAuthPamText, commonAuthPamText, passwordAuthPamText];
|
||||
let lockoutConfigured = false;
|
||||
let denyFromPam = -1;
|
||||
for (let i = 0; i < pamSources.length; i++) {
|
||||
const source = pamSources[i];
|
||||
if (!source)
|
||||
continue;
|
||||
if (usesPamLockoutPolicy(source))
|
||||
lockoutConfigured = true;
|
||||
const denyValue = parsePamLineDenyValue(source);
|
||||
if (denyValue >= 0 && (denyFromPam < 0 || denyValue < denyFromPam))
|
||||
denyFromPam = denyValue;
|
||||
}
|
||||
|
||||
if (!lockoutConfigured) {
|
||||
passwordAttemptLimitHint = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const denyFromConfig = parseFaillockDenyValue(faillockConfigText);
|
||||
if (denyFromConfig >= 0) {
|
||||
passwordAttemptLimitHint = denyFromConfig;
|
||||
return;
|
||||
}
|
||||
|
||||
if (denyFromPam >= 0) {
|
||||
passwordAttemptLimitHint = denyFromPam;
|
||||
return;
|
||||
}
|
||||
|
||||
// pam_faillock default deny value when no explicit config is set.
|
||||
passwordAttemptLimitHint = 3;
|
||||
}
|
||||
|
||||
function isLikelyLockoutMessage(message) {
|
||||
const lower = (message || "").toLowerCase();
|
||||
return lower.includes("account is locked") || lower.includes("too many") || lower.includes("maximum number of") || lower.includes("auth_err");
|
||||
}
|
||||
|
||||
function currentAuthMessage() {
|
||||
if (GreeterState.pamState === "error")
|
||||
return "Authentication error - try again";
|
||||
if (GreeterState.pamState === "max")
|
||||
return "Too many failed attempts - account may be locked";
|
||||
if (GreeterState.pamState === "fail") {
|
||||
if (passwordAttemptLimitHint > 0) {
|
||||
const attempt = Math.max(1, Math.min(passwordFailureCount, passwordAttemptLimitHint));
|
||||
const remaining = Math.max(passwordAttemptLimitHint - attempt, 0);
|
||||
if (remaining > 0) {
|
||||
return "Incorrect password - attempt " + attempt + " of " + passwordAttemptLimitHint + " (lockout may follow)";
|
||||
}
|
||||
return "Incorrect password - next failures may trigger account lockout";
|
||||
}
|
||||
return "Incorrect password";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function clearAuthFeedback() {
|
||||
GreeterState.pamState = "";
|
||||
authFeedbackMessage = "";
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GreetdSettings
|
||||
function onSettingsLoadedChanged() {
|
||||
if (GreetdSettings.settingsLoaded)
|
||||
if (GreetdSettings.settingsLoaded) {
|
||||
initWeatherService();
|
||||
if (isPrimaryScreen) {
|
||||
applyLastSuccessfulUser();
|
||||
finalizeSessionSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onRememberLastUserChanged() {
|
||||
if (!isPrimaryScreen)
|
||||
return;
|
||||
if (!GreetdSettings.rememberLastUser && GreetdMemory.lastSuccessfulUser) {
|
||||
GreetdMemory.setLastSuccessfulUser("");
|
||||
}
|
||||
applyLastSuccessfulUser();
|
||||
}
|
||||
|
||||
function onRememberLastSessionChanged() {
|
||||
if (!isPrimaryScreen)
|
||||
return;
|
||||
if (!GreetdSettings.rememberLastSession && GreetdMemory.lastSessionId) {
|
||||
GreetdMemory.setLastSessionId("");
|
||||
}
|
||||
finalizeSessionSelection();
|
||||
}
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: greetdPamWatcher
|
||||
path: "/etc/pam.d/greetd"
|
||||
printErrors: false
|
||||
onLoaded: {
|
||||
root.greetdPamText = text();
|
||||
root.refreshPasswordAttemptPolicyHint();
|
||||
}
|
||||
onLoadFailed: {
|
||||
root.greetdPamText = "";
|
||||
root.refreshPasswordAttemptPolicyHint();
|
||||
}
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: systemAuthPamWatcher
|
||||
path: "/etc/pam.d/system-auth"
|
||||
printErrors: false
|
||||
onLoaded: {
|
||||
root.systemAuthPamText = text();
|
||||
root.refreshPasswordAttemptPolicyHint();
|
||||
}
|
||||
onLoadFailed: {
|
||||
root.systemAuthPamText = "";
|
||||
root.refreshPasswordAttemptPolicyHint();
|
||||
}
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: commonAuthPamWatcher
|
||||
path: "/etc/pam.d/common-auth"
|
||||
printErrors: false
|
||||
onLoaded: {
|
||||
root.commonAuthPamText = text();
|
||||
root.refreshPasswordAttemptPolicyHint();
|
||||
}
|
||||
onLoadFailed: {
|
||||
root.commonAuthPamText = "";
|
||||
root.refreshPasswordAttemptPolicyHint();
|
||||
}
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: passwordAuthPamWatcher
|
||||
path: "/etc/pam.d/password-auth"
|
||||
printErrors: false
|
||||
onLoaded: {
|
||||
root.passwordAuthPamText = text();
|
||||
root.refreshPasswordAttemptPolicyHint();
|
||||
}
|
||||
onLoadFailed: {
|
||||
root.passwordAuthPamText = "";
|
||||
root.refreshPasswordAttemptPolicyHint();
|
||||
}
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: faillockConfigWatcher
|
||||
path: "/etc/security/faillock.conf"
|
||||
printErrors: false
|
||||
onLoaded: {
|
||||
root.faillockConfigText = text();
|
||||
root.refreshPasswordAttemptPolicyHint();
|
||||
}
|
||||
onLoadFailed: {
|
||||
root.faillockConfigText = "";
|
||||
root.refreshPasswordAttemptPolicyHint();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
initWeatherService();
|
||||
refreshPasswordAttemptPolicyHint();
|
||||
|
||||
if (isPrimaryScreen)
|
||||
applyLastSuccessfulUser();
|
||||
@@ -63,6 +296,8 @@ Item {
|
||||
}
|
||||
|
||||
function applyLastSuccessfulUser() {
|
||||
if (!GreetdSettings.settingsLoaded || !GreetdSettings.rememberLastUser)
|
||||
return;
|
||||
const lastUser = GreetdMemory.lastSuccessfulUser;
|
||||
if (lastUser && !GreeterState.showPasswordInput && !GreeterState.username) {
|
||||
GreeterState.username = lastUser;
|
||||
@@ -72,6 +307,38 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function submitUsername(rawValue) {
|
||||
const user = (rawValue || "").trim();
|
||||
if (!user)
|
||||
return;
|
||||
if (GreeterState.username !== user) {
|
||||
passwordFailureCount = 0;
|
||||
clearAuthFeedback();
|
||||
}
|
||||
GreeterState.username = user;
|
||||
GreeterState.showPasswordInput = true;
|
||||
PortalService.getGreeterUserProfileImage(user);
|
||||
GreeterState.passwordBuffer = "";
|
||||
}
|
||||
|
||||
function startAuthSession() {
|
||||
if (!GreeterState.showPasswordInput || !GreeterState.username)
|
||||
return;
|
||||
if (GreeterState.unlocking || Greetd.state !== GreetdState.Inactive)
|
||||
return;
|
||||
if (!GreeterState.passwordBuffer || GreeterState.passwordBuffer.length === 0)
|
||||
return;
|
||||
awaitingExternalAuth = false;
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
authTimeout.restart();
|
||||
Greetd.createSession(GreeterState.username);
|
||||
}
|
||||
|
||||
function isExternalAuthPrompt(message, responseRequired) {
|
||||
// Non-response PAM messages commonly represent waiting states (fprint/U2F/token touch).
|
||||
return !responseRequired;
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
if (weatherInitialized)
|
||||
WeatherService.removeRef();
|
||||
@@ -421,15 +688,10 @@ Item {
|
||||
}
|
||||
onAccepted: {
|
||||
if (GreeterState.showPasswordInput) {
|
||||
if (Greetd.state === GreetdState.Inactive && GreeterState.username) {
|
||||
Greetd.createSession(GreeterState.username);
|
||||
}
|
||||
root.startAuthSession();
|
||||
} else {
|
||||
if (text.trim()) {
|
||||
GreeterState.username = text.trim();
|
||||
GreeterState.showPasswordInput = true;
|
||||
PortalService.getGreeterUserProfileImage(GreeterState.username);
|
||||
GreeterState.passwordBuffer = "";
|
||||
root.submitUsername(text);
|
||||
syncingFromState = true;
|
||||
text = "";
|
||||
syncingFromState = false;
|
||||
@@ -568,15 +830,10 @@ Item {
|
||||
enabled: true
|
||||
onClicked: {
|
||||
if (GreeterState.showPasswordInput) {
|
||||
if (GreeterState.username) {
|
||||
Greetd.createSession(GreeterState.username);
|
||||
}
|
||||
root.startAuthSession();
|
||||
} else {
|
||||
if (inputField.text.trim()) {
|
||||
GreeterState.username = inputField.text.trim();
|
||||
GreeterState.showPasswordInput = true;
|
||||
PortalService.getGreeterUserProfileImage(GreeterState.username);
|
||||
GreeterState.passwordBuffer = "";
|
||||
root.submitUsername(inputField.text);
|
||||
inputField.text = "";
|
||||
}
|
||||
}
|
||||
@@ -601,20 +858,16 @@ Item {
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 20
|
||||
Layout.preferredHeight: 38
|
||||
Layout.topMargin: -Theme.spacingS
|
||||
Layout.bottomMargin: -Theme.spacingS
|
||||
text: {
|
||||
if (GreeterState.pamState === "error")
|
||||
return "Authentication error - try again";
|
||||
if (GreeterState.pamState === "fail")
|
||||
return "Incorrect password";
|
||||
return "";
|
||||
}
|
||||
text: root.authFeedbackMessage
|
||||
color: Theme.error
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
opacity: GreeterState.pamState !== "" ? 1 : 0
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 2
|
||||
opacity: root.authFeedbackMessage !== "" ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
@@ -1029,9 +1282,11 @@ Item {
|
||||
return;
|
||||
if (!GreetdMemory.memoryReady)
|
||||
return;
|
||||
if (!GreetdSettings.settingsLoaded)
|
||||
return;
|
||||
|
||||
const savedSession = GreetdMemory.lastSessionId;
|
||||
if (savedSession) {
|
||||
const savedSession = GreetdSettings.rememberLastSession ? GreetdMemory.lastSessionId : "";
|
||||
if (savedSession && GreetdSettings.rememberLastSession) {
|
||||
for (var i = 0; i < GreeterState.sessionPaths.length; i++) {
|
||||
if (GreeterState.sessionPaths[i] === savedSession) {
|
||||
GreeterState.currentSessionIndex = i;
|
||||
@@ -1163,45 +1418,100 @@ Item {
|
||||
enabled: isPrimaryScreen
|
||||
|
||||
function onAuthMessage(message, error, responseRequired, echoResponse) {
|
||||
awaitingExternalAuth = root.isExternalAuthPrompt(message, responseRequired);
|
||||
authTimeout.interval = awaitingExternalAuth ? externalAuthTimeoutMs : defaultAuthTimeoutMs;
|
||||
authTimeout.restart();
|
||||
if (responseRequired) {
|
||||
Greetd.respond(GreeterState.passwordBuffer);
|
||||
GreeterState.passwordBuffer = "";
|
||||
inputField.text = "";
|
||||
return;
|
||||
}
|
||||
if (!error)
|
||||
Greetd.respond("");
|
||||
Greetd.respond("");
|
||||
}
|
||||
|
||||
function onStateChanged() {
|
||||
if (Greetd.state === GreetdState.Inactive) {
|
||||
awaitingExternalAuth = false;
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
authTimeout.stop();
|
||||
}
|
||||
}
|
||||
|
||||
function onReadyToLaunch() {
|
||||
awaitingExternalAuth = false;
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
authTimeout.stop();
|
||||
passwordFailureCount = 0;
|
||||
clearAuthFeedback();
|
||||
const sessionCmd = GreeterState.selectedSession || GreeterState.sessionExecs[GreeterState.currentSessionIndex];
|
||||
const sessionPath = GreeterState.selectedSessionPath || GreeterState.sessionPaths[GreeterState.currentSessionIndex];
|
||||
if (!sessionCmd) {
|
||||
GreeterState.pamState = "error";
|
||||
authFeedbackMessage = currentAuthMessage();
|
||||
placeholderDelay.restart();
|
||||
return;
|
||||
}
|
||||
|
||||
GreeterState.unlocking = true;
|
||||
launchTimeout.restart();
|
||||
GreetdMemory.setLastSessionId(sessionPath);
|
||||
GreetdMemory.setLastSuccessfulUser(GreeterState.username);
|
||||
if (GreetdSettings.rememberLastSession) {
|
||||
GreetdMemory.setLastSessionId(sessionPath);
|
||||
} else if (GreetdMemory.lastSessionId) {
|
||||
GreetdMemory.setLastSessionId("");
|
||||
}
|
||||
if (GreetdSettings.rememberLastUser) {
|
||||
GreetdMemory.setLastSuccessfulUser(GreeterState.username);
|
||||
} else if (GreetdMemory.lastSuccessfulUser) {
|
||||
GreetdMemory.setLastSuccessfulUser("");
|
||||
}
|
||||
Greetd.launch(sessionCmd.split(" "), ["XDG_SESSION_TYPE=wayland"]);
|
||||
}
|
||||
|
||||
function onAuthFailure(message) {
|
||||
awaitingExternalAuth = false;
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
authTimeout.stop();
|
||||
launchTimeout.stop();
|
||||
GreeterState.unlocking = false;
|
||||
GreeterState.pamState = "fail";
|
||||
if (isLikelyLockoutMessage(message)) {
|
||||
GreeterState.pamState = "max";
|
||||
} else {
|
||||
GreeterState.pamState = "fail";
|
||||
passwordFailureCount = passwordFailureCount + 1;
|
||||
}
|
||||
authFeedbackMessage = currentAuthMessage();
|
||||
GreeterState.passwordBuffer = "";
|
||||
inputField.text = "";
|
||||
placeholderDelay.restart();
|
||||
Greetd.cancelSession();
|
||||
}
|
||||
|
||||
function onError(error) {
|
||||
awaitingExternalAuth = false;
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
authTimeout.stop();
|
||||
launchTimeout.stop();
|
||||
GreeterState.unlocking = false;
|
||||
GreeterState.pamState = "error";
|
||||
authFeedbackMessage = currentAuthMessage();
|
||||
GreeterState.passwordBuffer = "";
|
||||
inputField.text = "";
|
||||
placeholderDelay.restart();
|
||||
Greetd.cancelSession();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: authTimeout
|
||||
interval: defaultAuthTimeoutMs
|
||||
onTriggered: {
|
||||
if (GreeterState.unlocking || Greetd.state === GreetdState.Inactive)
|
||||
return;
|
||||
awaitingExternalAuth = false;
|
||||
authTimeout.interval = defaultAuthTimeoutMs;
|
||||
GreeterState.pamState = "error";
|
||||
authFeedbackMessage = currentAuthMessage();
|
||||
GreeterState.passwordBuffer = "";
|
||||
inputField.text = "";
|
||||
placeholderDelay.restart();
|
||||
@@ -1217,6 +1527,7 @@ Item {
|
||||
return;
|
||||
GreeterState.unlocking = false;
|
||||
GreeterState.pamState = "error";
|
||||
authFeedbackMessage = currentAuthMessage();
|
||||
placeholderDelay.restart();
|
||||
Greetd.cancelSession();
|
||||
}
|
||||
@@ -1225,7 +1536,7 @@ Item {
|
||||
Timer {
|
||||
id: placeholderDelay
|
||||
interval: 4000
|
||||
onTriggered: GreeterState.pamState = ""
|
||||
onTriggered: clearAuthFeedback()
|
||||
}
|
||||
|
||||
LockPowerMenu {
|
||||
|
||||
@@ -9,6 +9,7 @@ A greeter for [greetd](https://github.com/kennylevinsen/greetd) that follows the
|
||||
- **Multiple compositors**: Supports niri, Hyprland, Sway, or mangowc.
|
||||
- **Custom PAM**: Supports custom PAM configuration in `/etc/pam.d/greetd`
|
||||
- **Session Memory**: Remembers last selected session and user
|
||||
- Can be disabled via `settings.json` keys: `greeterRememberLastSession` and `greeterRememberLastUser`
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -212,6 +213,7 @@ dms-greeter --command hyprland
|
||||
dms-greeter --command sway
|
||||
dms-greeter --command mangowc
|
||||
dms-greeter --command niri -C /path/to/custom-niri.kdl
|
||||
dms-greeter --command niri --remember-last-user false --remember-last-session false
|
||||
```
|
||||
|
||||
Configure greetd to use it in `/etc/greetd/config.toml`:
|
||||
|
||||
@@ -6,6 +6,9 @@ COMPOSITOR=""
|
||||
COMPOSITOR_CONFIG=""
|
||||
DMS_PATH="dms-greeter"
|
||||
CACHE_DIR="/var/cache/dms-greeter"
|
||||
REMEMBER_LAST_SESSION=""
|
||||
REMEMBER_LAST_USER=""
|
||||
DEBUG_MODE=0
|
||||
|
||||
show_help() {
|
||||
cat << EOF
|
||||
@@ -22,6 +25,15 @@ Options:
|
||||
(default: dms-greeter)
|
||||
--cache-dir PATH Cache directory for greeter data
|
||||
(default: /var/cache/dms-greeter)
|
||||
--remember-last-session BOOL
|
||||
Persist selected session to greeter memory
|
||||
(BOOL: true/false, default: from settings.json)
|
||||
--remember-last-user BOOL
|
||||
Persist last successful username to greeter memory
|
||||
(BOOL: true/false, default: from settings.json)
|
||||
--no-save-session Alias for --remember-last-session false
|
||||
--no-save-username Alias for --remember-last-user false
|
||||
--debug Enable verbose startup logging to stderr
|
||||
-h, --help Show this help message
|
||||
|
||||
Examples:
|
||||
@@ -30,6 +42,7 @@ Examples:
|
||||
dms-greeter --command sway -p /home/user/.config/quickshell/custom-dms
|
||||
dms-greeter --command scroll -p /home/user/.config/quickshell/custom-dms
|
||||
dms-greeter --command niri --cache-dir /tmp/dmsgreeter
|
||||
dms-greeter --command niri --no-save-session --no-save-username
|
||||
dms-greeter --command mango
|
||||
dms-greeter --command labwc
|
||||
EOF
|
||||
@@ -43,6 +56,41 @@ require_command() {
|
||||
fi
|
||||
}
|
||||
|
||||
normalize_bool_flag() {
|
||||
local flag_name="$1"
|
||||
local value="$2"
|
||||
local normalized="${value,,}"
|
||||
|
||||
case "$normalized" in
|
||||
1|true|yes|on)
|
||||
echo "1"
|
||||
;;
|
||||
0|false|no|off)
|
||||
echo "0"
|
||||
;;
|
||||
*)
|
||||
echo "Error: $flag_name must be true/false (or 1/0, yes/no, on/off)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
exec_compositor() {
|
||||
local log_tag="$1"
|
||||
shift
|
||||
|
||||
if [[ "$DEBUG_MODE" == "1" ]]; then
|
||||
exec "$@"
|
||||
fi
|
||||
|
||||
if command -v systemd-cat >/dev/null 2>&1; then
|
||||
exec "$@" > >(systemd-cat -t "dms-greeter/$log_tag" -p info) 2>&1
|
||||
fi
|
||||
|
||||
local log_file="$CACHE_DIR/$log_tag.log"
|
||||
exec "$@" >> "$log_file" 2>&1
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--command)
|
||||
@@ -61,6 +109,26 @@ while [[ $# -gt 0 ]]; do
|
||||
CACHE_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--remember-last-session)
|
||||
REMEMBER_LAST_SESSION="$2"
|
||||
shift 2
|
||||
;;
|
||||
--remember-last-user)
|
||||
REMEMBER_LAST_USER="$2"
|
||||
shift 2
|
||||
;;
|
||||
--no-save-session)
|
||||
REMEMBER_LAST_SESSION="0"
|
||||
shift
|
||||
;;
|
||||
--no-save-username)
|
||||
REMEMBER_LAST_USER="0"
|
||||
shift
|
||||
;;
|
||||
--debug)
|
||||
DEBUG_MODE=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
@@ -113,8 +181,38 @@ export EGL_PLATFORM=gbm
|
||||
export DMS_RUN_GREETER=1
|
||||
export DMS_GREET_CFG_DIR="$CACHE_DIR"
|
||||
|
||||
if [[ -n "$REMEMBER_LAST_SESSION" ]]; then
|
||||
DMS_GREET_REMEMBER_LAST_SESSION=$(normalize_bool_flag "--remember-last-session" "$REMEMBER_LAST_SESSION")
|
||||
export DMS_GREET_REMEMBER_LAST_SESSION
|
||||
if [[ "$DMS_GREET_REMEMBER_LAST_SESSION" == "1" ]]; then
|
||||
DMS_SAVE_SESSION=true
|
||||
else
|
||||
DMS_SAVE_SESSION=false
|
||||
fi
|
||||
export DMS_SAVE_SESSION
|
||||
fi
|
||||
|
||||
if [[ -n "$REMEMBER_LAST_USER" ]]; then
|
||||
DMS_GREET_REMEMBER_LAST_USER=$(normalize_bool_flag "--remember-last-user" "$REMEMBER_LAST_USER")
|
||||
export DMS_GREET_REMEMBER_LAST_USER
|
||||
if [[ "$DMS_GREET_REMEMBER_LAST_USER" == "1" ]]; then
|
||||
DMS_SAVE_USERNAME=true
|
||||
else
|
||||
DMS_SAVE_USERNAME=false
|
||||
fi
|
||||
export DMS_SAVE_USERNAME
|
||||
fi
|
||||
|
||||
mkdir -p "$CACHE_DIR"
|
||||
|
||||
# Keep greeter VT clean by default; callers can override via env or --debug.
|
||||
if [[ -z "${RUST_LOG:-}" ]]; then
|
||||
export RUST_LOG=warn
|
||||
fi
|
||||
if [[ -z "${NIRI_LOG:-}" ]]; then
|
||||
export NIRI_LOG=warn
|
||||
fi
|
||||
|
||||
if command -v qs >/dev/null 2>&1; then
|
||||
QS_BIN="qs"
|
||||
elif command -v quickshell >/dev/null 2>&1; then
|
||||
@@ -130,7 +228,9 @@ if [[ "$DMS_PATH" == /* ]]; then
|
||||
else
|
||||
RESOLVED_PATH=$(locate_dms_config "$DMS_PATH")
|
||||
if [[ $? -eq 0 && -n "$RESOLVED_PATH" ]]; then
|
||||
echo "Located DMS config at: $RESOLVED_PATH" >&2
|
||||
if [[ "$DEBUG_MODE" == "1" ]]; then
|
||||
echo "Located DMS config at: $RESOLVED_PATH" >&2
|
||||
fi
|
||||
QS_CMD="$QS_BIN -p $RESOLVED_PATH"
|
||||
else
|
||||
echo "Error: Could not find DMS config '$DMS_PATH' (shell.qml) in any valid config path" >&2
|
||||
@@ -192,7 +292,7 @@ NIRI_EOF
|
||||
spawn-at-startup "sh" "-c" "$QS_CMD; niri msg action quit --skip-confirmation"
|
||||
NIRI_EOF
|
||||
COMPOSITOR_CONFIG="$TEMP_CONFIG"
|
||||
exec niri -c "$COMPOSITOR_CONFIG"
|
||||
exec_compositor "niri" niri -c "$COMPOSITOR_CONFIG"
|
||||
;;
|
||||
|
||||
hyprland)
|
||||
@@ -222,9 +322,9 @@ HYPRLAND_EOF
|
||||
COMPOSITOR_CONFIG="$TEMP_CONFIG"
|
||||
fi
|
||||
if command -v start-hyprland >/dev/null 2>&1; then
|
||||
exec start-hyprland -- --config "$COMPOSITOR_CONFIG"
|
||||
exec_compositor "hyprland" start-hyprland -- --config "$COMPOSITOR_CONFIG"
|
||||
else
|
||||
exec Hyprland -c "$COMPOSITOR_CONFIG"
|
||||
exec_compositor "hyprland" Hyprland -c "$COMPOSITOR_CONFIG"
|
||||
fi
|
||||
;;
|
||||
|
||||
@@ -245,7 +345,7 @@ exec "$QS_CMD; swaymsg exit"
|
||||
SWAY_EOF
|
||||
COMPOSITOR_CONFIG="$TEMP_CONFIG"
|
||||
fi
|
||||
exec sway --unsupported-gpu -c "$COMPOSITOR_CONFIG"
|
||||
exec_compositor "sway" sway --unsupported-gpu -c "$COMPOSITOR_CONFIG"
|
||||
;;
|
||||
|
||||
scroll)
|
||||
@@ -265,7 +365,7 @@ exec "$QS_CMD; scrollmsg exit"
|
||||
SCROLL_EOF
|
||||
COMPOSITOR_CONFIG="$TEMP_CONFIG"
|
||||
fi
|
||||
exec scroll -c "$COMPOSITOR_CONFIG"
|
||||
exec_compositor "scroll" scroll -c "$COMPOSITOR_CONFIG"
|
||||
;;
|
||||
|
||||
miracle|miracle-wm)
|
||||
@@ -285,24 +385,24 @@ exec "$QS_CMD; miraclemsg exit"
|
||||
MIRACLE_EOF
|
||||
COMPOSITOR_CONFIG="$TEMP_CONFIG"
|
||||
fi
|
||||
exec miracle-wm -c "$COMPOSITOR_CONFIG"
|
||||
exec_compositor "miracle" miracle-wm -c "$COMPOSITOR_CONFIG"
|
||||
;;
|
||||
|
||||
labwc)
|
||||
require_command "labwc"
|
||||
if [[ -n "$COMPOSITOR_CONFIG" ]]; then
|
||||
exec labwc --config "$COMPOSITOR_CONFIG" --session "$QS_CMD"
|
||||
exec_compositor "labwc" labwc --config "$COMPOSITOR_CONFIG" --session "$QS_CMD"
|
||||
else
|
||||
exec labwc --session "$QS_CMD"
|
||||
exec_compositor "labwc" labwc --session "$QS_CMD"
|
||||
fi
|
||||
;;
|
||||
|
||||
mango|mangowc)
|
||||
require_command "mango"
|
||||
if [[ -n "$COMPOSITOR_CONFIG" ]]; then
|
||||
exec mango -c "$COMPOSITOR_CONFIG" -s "$QS_CMD && mmsg -d quit"
|
||||
exec_compositor "mango" mango -c "$COMPOSITOR_CONFIG" -s "$QS_CMD && mmsg -d quit"
|
||||
else
|
||||
exec mango -s "$QS_CMD && mmsg -d quit"
|
||||
exec_compositor "mango" mango -s "$QS_CMD && mmsg -d quit"
|
||||
fi
|
||||
;;
|
||||
|
||||
|
||||
@@ -745,8 +745,7 @@ Item {
|
||||
}
|
||||
}
|
||||
onAccepted: {
|
||||
if (!demoMode && !pam.passwd.active) {
|
||||
console.log("Enter pressed, starting PAM authentication");
|
||||
if (!demoMode && !root.unlocking && !pam.passwd.active && !pam.u2fPending) {
|
||||
pam.passwd.start();
|
||||
}
|
||||
}
|
||||
@@ -755,6 +754,11 @@ Item {
|
||||
return;
|
||||
}
|
||||
|
||||
if (root.unlocking) {
|
||||
event.accepted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
clear();
|
||||
}
|
||||
@@ -998,8 +1002,7 @@ Item {
|
||||
visible: (demoMode || (!pam.passwd.active && !root.unlocking))
|
||||
enabled: !demoMode
|
||||
onClicked: {
|
||||
if (!demoMode) {
|
||||
console.log("Enter button clicked, starting PAM authentication");
|
||||
if (!demoMode && !root.unlocking && !pam.u2fPending) {
|
||||
pam.passwd.start();
|
||||
}
|
||||
}
|
||||
@@ -1602,6 +1605,7 @@ Item {
|
||||
onStateChanged: {
|
||||
root.pamState = state;
|
||||
if (state !== "") {
|
||||
root.unlocking = false;
|
||||
placeholderDelay.restart();
|
||||
passwordField.text = "";
|
||||
root.passwordBuffer = "";
|
||||
@@ -1609,6 +1613,15 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: pam
|
||||
|
||||
function onUnlockInProgressChanged() {
|
||||
if (!pam.unlockInProgress && root.unlocking)
|
||||
root.unlocking = false;
|
||||
}
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: pam
|
||||
property: "buffer"
|
||||
|
||||
@@ -22,6 +22,64 @@ Scope {
|
||||
signal flashMsg
|
||||
signal unlockRequested
|
||||
|
||||
function resetAuthFlows(): void {
|
||||
passwd.abort();
|
||||
fprint.abort();
|
||||
u2f.abort();
|
||||
errorRetry.running = false;
|
||||
u2fErrorRetry.running = false;
|
||||
u2fPendingTimeout.running = false;
|
||||
passwdActiveTimeout.running = false;
|
||||
unlockRequestTimeout.running = false;
|
||||
u2fPending = false;
|
||||
u2fState = "";
|
||||
unlockInProgress = false;
|
||||
}
|
||||
|
||||
function recoverFromAuthStall(newState: string): void {
|
||||
resetAuthFlows();
|
||||
state = newState;
|
||||
flashMsg();
|
||||
stateReset.restart();
|
||||
fprint.checkAvail();
|
||||
u2f.checkAvail();
|
||||
}
|
||||
|
||||
function completeUnlock(): void {
|
||||
if (!unlockInProgress) {
|
||||
unlockInProgress = true;
|
||||
passwd.abort();
|
||||
fprint.abort();
|
||||
u2f.abort();
|
||||
errorRetry.running = false;
|
||||
u2fErrorRetry.running = false;
|
||||
u2fPendingTimeout.running = false;
|
||||
u2fPending = false;
|
||||
u2fState = "";
|
||||
unlockRequestTimeout.restart();
|
||||
unlockRequested();
|
||||
}
|
||||
}
|
||||
|
||||
function proceedAfterPrimaryAuth(): void {
|
||||
if (SettingsData.enableU2f && SettingsData.u2fMode === "and" && u2f.available) {
|
||||
u2f.startForSecondFactor();
|
||||
} else {
|
||||
completeUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
function cancelU2fPending(): void {
|
||||
if (!u2fPending)
|
||||
return;
|
||||
u2f.abort();
|
||||
u2fErrorRetry.running = false;
|
||||
u2fPendingTimeout.running = false;
|
||||
u2fPending = false;
|
||||
u2fState = "";
|
||||
fprint.checkAvail();
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: dankshellConfigWatcher
|
||||
|
||||
@@ -66,6 +124,13 @@ Scope {
|
||||
return;
|
||||
}
|
||||
|
||||
unlockRequestTimeout.running = false;
|
||||
root.unlockInProgress = false;
|
||||
root.u2fPending = false;
|
||||
root.u2fState = "";
|
||||
u2fPendingTimeout.running = false;
|
||||
u2f.abort();
|
||||
|
||||
if (res === PamResult.Error)
|
||||
root.state = "error";
|
||||
else if (res === PamResult.MaxTries)
|
||||
@@ -78,6 +143,18 @@ Scope {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: passwd
|
||||
|
||||
function onActiveChanged() {
|
||||
if (passwd.active) {
|
||||
passwdActiveTimeout.restart();
|
||||
} else {
|
||||
passwdActiveTimeout.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PamContext {
|
||||
id: fprint
|
||||
|
||||
@@ -152,6 +229,40 @@ Scope {
|
||||
onTriggered: fprint.start()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: u2fErrorRetry
|
||||
|
||||
interval: 800
|
||||
onTriggered: u2f.start()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: u2fPendingTimeout
|
||||
|
||||
interval: 30000
|
||||
onTriggered: root.cancelU2fPending()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: passwdActiveTimeout
|
||||
|
||||
interval: 15000
|
||||
onTriggered: {
|
||||
if (passwd.active)
|
||||
root.recoverFromAuthStall("error");
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: unlockRequestTimeout
|
||||
|
||||
interval: 8000
|
||||
onTriggered: {
|
||||
if (root.unlockInProgress)
|
||||
root.recoverFromAuthStall("error");
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: stateReset
|
||||
|
||||
@@ -178,11 +289,9 @@ Scope {
|
||||
root.state = "";
|
||||
root.fprintState = "";
|
||||
root.lockMessage = "";
|
||||
root.unlockInProgress = false;
|
||||
root.resetAuthFlows();
|
||||
} else {
|
||||
fprint.abort();
|
||||
passwd.abort();
|
||||
root.unlockInProgress = false;
|
||||
root.resetAuthFlows();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,5 +301,21 @@ Scope {
|
||||
function onEnableFprintChanged(): void {
|
||||
fprint.checkAvail();
|
||||
}
|
||||
|
||||
function onEnableU2fChanged(): void {
|
||||
u2f.checkAvail();
|
||||
}
|
||||
|
||||
function onU2fModeChanged(): void {
|
||||
if (root.lockSecured) {
|
||||
u2f.abort();
|
||||
u2fErrorRetry.running = false;
|
||||
u2fPendingTimeout.running = false;
|
||||
unlockRequestTimeout.running = false;
|
||||
root.u2fPending = false;
|
||||
root.u2fState = "";
|
||||
u2f.checkAvail();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user