mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-02 03:28:28 -04:00
feat: new PAM auth management & settings in greeter/lockscreen
- Introduce external management of greetd PAM - New functionality to validate and apply custom PAM service paths in lockscreen Port 1.5
This commit is contained in:
@@ -895,6 +895,10 @@ Singleton {
|
||||
readonly property bool greeterU2fReady: Processes.greeterU2fReady
|
||||
readonly property string greeterU2fReason: Processes.greeterU2fReason
|
||||
readonly property string greeterU2fSource: Processes.greeterU2fSource
|
||||
property string lockPamPath: ""
|
||||
property bool lockPamInlineFprint: false
|
||||
property bool lockPamInlineU2f: false
|
||||
property bool greeterPamExternallyManaged: false
|
||||
property string lockScreenInactiveColor: "#000000"
|
||||
property int lockScreenNotificationMode: 0
|
||||
property bool lockScreenVideoEnabled: false
|
||||
|
||||
@@ -233,6 +233,7 @@ var SPEC = {
|
||||
greeterLockDateFormat: { def: "", onChange: "markGreeterSyncPending" },
|
||||
greeterFontFamily: { def: "", onChange: "markGreeterSyncPending" },
|
||||
greeterWallpaperFillMode: { def: "", onChange: "markGreeterSyncPending" },
|
||||
greeterPamExternallyManaged: { def: false, onChange: "markGreeterSyncPending" },
|
||||
greeterSyncPending: { def: false },
|
||||
greeterSyncBaseline: { def: {} },
|
||||
mediaSize: { def: 1 },
|
||||
@@ -455,6 +456,9 @@ var SPEC = {
|
||||
maxFprintTries: { def: 15 },
|
||||
enableU2f: { def: false, onChange: "scheduleAuthApply" },
|
||||
u2fMode: { def: "or" },
|
||||
lockPamPath: { def: "" },
|
||||
lockPamInlineFprint: { def: false },
|
||||
lockPamInlineU2f: { def: false },
|
||||
lockScreenInactiveColor: { def: "#000000" },
|
||||
lockScreenNotificationMode: { def: 0 },
|
||||
lockScreenVideoEnabled: { def: false },
|
||||
|
||||
@@ -90,6 +90,17 @@ Scope {
|
||||
fprint.checkAvail();
|
||||
}
|
||||
|
||||
readonly property bool customPamActive: SettingsData.lockPamPath !== "" && customPamWatcher.loaded
|
||||
readonly property bool fprintSuppressedByCustomPam: customPamActive && SettingsData.lockPamInlineFprint
|
||||
readonly property bool u2fSuppressedByCustomPam: customPamActive && SettingsData.lockPamInlineU2f
|
||||
|
||||
FileView {
|
||||
id: customPamWatcher
|
||||
|
||||
path: SettingsData.lockPamPath !== "" ? SettingsData.lockPamPath : ""
|
||||
printErrors: false
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: dankshellConfigWatcher
|
||||
|
||||
@@ -148,6 +159,8 @@ Scope {
|
||||
id: passwd
|
||||
|
||||
config: {
|
||||
if (root.customPamActive)
|
||||
return SettingsData.lockPamPath.slice(SettingsData.lockPamPath.lastIndexOf("/") + 1);
|
||||
if (dankshellConfigWatcher.loaded)
|
||||
return "dankshell";
|
||||
if (nixosMarker.loaded || root.runningFromNixStore)
|
||||
@@ -157,6 +170,10 @@ Scope {
|
||||
return "login";
|
||||
}
|
||||
configDirectory: {
|
||||
if (root.customPamActive) {
|
||||
const idx = SettingsData.lockPamPath.lastIndexOf("/");
|
||||
return idx > 0 ? SettingsData.lockPamPath.slice(0, idx) : "/";
|
||||
}
|
||||
if (dankshellConfigWatcher.loaded)
|
||||
return "/etc/pam.d";
|
||||
if (nixosMarker.loaded || root.runningFromNixStore)
|
||||
@@ -248,7 +265,7 @@ Scope {
|
||||
property int errorTries
|
||||
|
||||
function checkAvail(): void {
|
||||
if (!available || !SettingsData.enableFprint || !root.lockSecured) {
|
||||
if (!available || !SettingsData.enableFprint || !root.lockSecured || root.fprintSuppressedByCustomPam) {
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
@@ -308,7 +325,7 @@ Scope {
|
||||
property bool available: SettingsData.lockU2fReady
|
||||
|
||||
function checkAvail(): void {
|
||||
if (!available || !SettingsData.enableU2f || !root.lockSecured) {
|
||||
if (!available || !SettingsData.enableU2f || !root.lockSecured || root.u2fSuppressedByCustomPam) {
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
@@ -318,7 +335,7 @@ Scope {
|
||||
}
|
||||
|
||||
function startForSecondFactor(): void {
|
||||
if (!available || !SettingsData.enableU2f) {
|
||||
if (!available || !SettingsData.enableU2f || root.u2fSuppressedByCustomPam) {
|
||||
root.completeUnlock();
|
||||
return;
|
||||
}
|
||||
@@ -331,7 +348,7 @@ Scope {
|
||||
}
|
||||
|
||||
function startForAlternativeAuth(): void {
|
||||
if (!available || !SettingsData.enableU2f || SettingsData.u2fMode !== "or" || root.unlockInProgress || passwd.active || active)
|
||||
if (!available || !SettingsData.enableU2f || root.u2fSuppressedByCustomPam || SettingsData.u2fMode !== "or" || root.unlockInProgress || passwd.active || active)
|
||||
return;
|
||||
abort();
|
||||
root.u2fPending = true;
|
||||
@@ -486,6 +503,19 @@ Scope {
|
||||
u2f.checkAvail();
|
||||
}
|
||||
|
||||
function onLockPamPathChanged(): void {
|
||||
fprint.checkAvail();
|
||||
u2f.checkAvail();
|
||||
}
|
||||
|
||||
function onLockPamInlineFprintChanged(): void {
|
||||
fprint.checkAvail();
|
||||
}
|
||||
|
||||
function onLockPamInlineU2fChanged(): void {
|
||||
u2f.checkAvail();
|
||||
}
|
||||
|
||||
function onU2fModeChanged(): void {
|
||||
if (root.lockSecured) {
|
||||
u2f.abort();
|
||||
|
||||
@@ -18,6 +18,9 @@ Item {
|
||||
readonly property bool greeterU2fToggleAvailable: SettingsData.greeterU2fCanEnable || SettingsData.greeterEnableU2f
|
||||
|
||||
function greeterFingerprintDescription() {
|
||||
if (SettingsData.greeterPamExternallyManaged)
|
||||
return I18n.tr("greetd PAM is externally managed. DMS will not change fingerprint auth; configure it yourself.");
|
||||
|
||||
const source = SettingsData.greeterFingerprintSource;
|
||||
const reason = SettingsData.greeterFingerprintReason;
|
||||
|
||||
@@ -51,6 +54,9 @@ Item {
|
||||
}
|
||||
|
||||
function greeterU2fDescription() {
|
||||
if (SettingsData.greeterPamExternallyManaged)
|
||||
return I18n.tr("greetd PAM is externally managed. DMS will not change security-key auth; configure it yourself.");
|
||||
|
||||
const source = SettingsData.greeterU2fSource;
|
||||
const reason = SettingsData.greeterU2fReason;
|
||||
|
||||
@@ -499,6 +505,15 @@ Item {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "greeterPamExternallyManaged"
|
||||
tags: ["greeter", "pam", "managed", "external", "greetd", "auth"]
|
||||
text: I18n.tr("Manage greetd PAM automatically")
|
||||
description: SettingsData.greeterPamExternallyManaged ? I18n.tr("DMS never touches /etc/pam.d/greetd; any existing DMS block is removed on next sync. Configure fingerprint and security-key auth yourself.") : I18n.tr("DMS writes its managed block into /etc/pam.d/greetd during sync.")
|
||||
checked: !SettingsData.greeterPamExternallyManaged
|
||||
onToggled: checked => SettingsData.set("greeterPamExternallyManaged", !checked)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "greeterEnableFprint"
|
||||
tags: ["greeter", "fingerprint", "fprintd", "login", "auth"]
|
||||
@@ -506,7 +521,7 @@ Item {
|
||||
description: root.greeterFingerprintDescription()
|
||||
descriptionColor: (SettingsData.greeterFingerprintReason === "ready" || SettingsData.greeterFingerprintReason === "configured_externally") ? Theme.surfaceVariantText : Theme.warning
|
||||
checked: SettingsData.greeterEnableFprint
|
||||
enabled: root.greeterFprintToggleAvailable
|
||||
enabled: root.greeterFprintToggleAvailable && !SettingsData.greeterPamExternallyManaged
|
||||
onToggled: checked => SettingsData.set("greeterEnableFprint", checked)
|
||||
}
|
||||
|
||||
@@ -517,7 +532,7 @@ Item {
|
||||
description: root.greeterU2fDescription()
|
||||
descriptionColor: (SettingsData.greeterU2fReason === "ready" || SettingsData.greeterU2fReason === "configured_externally") ? Theme.surfaceVariantText : Theme.warning
|
||||
checked: SettingsData.greeterEnableU2f
|
||||
enabled: root.greeterU2fToggleAvailable
|
||||
enabled: root.greeterU2fToggleAvailable && !SettingsData.greeterPamExternallyManaged
|
||||
onToggled: checked => SettingsData.set("greeterEnableU2f", checked)
|
||||
}
|
||||
}
|
||||
@@ -718,9 +733,13 @@ Item {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: !root.greeterSyncRunning && !root.greeterInstallActionRunning
|
||||
onClicked: root.runGreeterSync()
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.AllButtons
|
||||
cursorShape: !root.greeterSyncRunning && !root.greeterInstallActionRunning ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onClicked: mouse => {
|
||||
if (mouse.button === Qt.LeftButton && !root.greeterSyncRunning && !root.greeterInstallActionRunning)
|
||||
root.runGreeterSync();
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Modals.FileBrowser
|
||||
import qs.Services
|
||||
@@ -12,6 +13,71 @@ Item {
|
||||
readonly property bool lockFprintToggleAvailable: SettingsData.lockFingerprintCanEnable || SettingsData.enableFprint
|
||||
readonly property bool lockU2fToggleAvailable: SettingsData.lockU2fCanEnable || SettingsData.enableU2f
|
||||
|
||||
property var authServices: []
|
||||
property bool authValidateRunning: false
|
||||
property bool authValidateOk: false
|
||||
property bool authValidateWarn: false
|
||||
property string authValidateMessage: ""
|
||||
property string authPendingApplyPath: ""
|
||||
property bool authShowCustom: false
|
||||
|
||||
readonly property string authAutoLabel: I18n.tr("Auto (recommended)")
|
||||
readonly property string authCustomLabel: I18n.tr("Custom…")
|
||||
|
||||
function authServiceLabel(service) {
|
||||
const label = service.name.split("-").map(w => w.charAt(0).toUpperCase() + w.slice(1)).join("-");
|
||||
return service.dir === "/etc/pam.d" ? label : label + " (" + service.dir + ")";
|
||||
}
|
||||
|
||||
readonly property var authOptions: {
|
||||
var opts = [authAutoLabel];
|
||||
for (var i = 0; i < authServices.length; i++)
|
||||
opts.push(authServiceLabel(authServices[i]));
|
||||
opts.push(authCustomLabel);
|
||||
return opts;
|
||||
}
|
||||
|
||||
readonly property string authCurrentValue: {
|
||||
if (SettingsData.lockPamPath === "")
|
||||
return authAutoLabel;
|
||||
for (var i = 0; i < authServices.length; i++) {
|
||||
if (authServices[i].path === SettingsData.lockPamPath)
|
||||
return authServiceLabel(authServices[i]);
|
||||
}
|
||||
return authCustomLabel;
|
||||
}
|
||||
|
||||
function refreshAuthServices() {
|
||||
authListServicesProcess.running = true;
|
||||
}
|
||||
|
||||
function applyAutoAuthSource() {
|
||||
SettingsData.set("lockPamPath", "");
|
||||
SettingsData.set("lockPamInlineFprint", false);
|
||||
SettingsData.set("lockPamInlineU2f", false);
|
||||
root.authValidateOk = false;
|
||||
root.authValidateWarn = false;
|
||||
root.authValidateMessage = "";
|
||||
}
|
||||
|
||||
function validateAndApplyAuthSource(path) {
|
||||
if (!path || path === "")
|
||||
return;
|
||||
if (!path.startsWith("/")) {
|
||||
root.authValidateOk = false;
|
||||
root.authValidateWarn = false;
|
||||
root.authValidateMessage = I18n.tr("Not a valid path");
|
||||
return;
|
||||
}
|
||||
root.authPendingApplyPath = path;
|
||||
root.authValidateMessage = "";
|
||||
root.authValidateOk = false;
|
||||
root.authValidateWarn = false;
|
||||
root.authValidateRunning = true;
|
||||
authValidateProcess.command = ["dms", "auth", "validate", "--path", path, "--json"];
|
||||
authValidateProcess.running = true;
|
||||
}
|
||||
|
||||
function lockFingerprintDescription() {
|
||||
switch (SettingsData.lockFingerprintReason) {
|
||||
case "ready":
|
||||
@@ -48,10 +114,15 @@ Item {
|
||||
SettingsData.refreshAuthAvailability();
|
||||
}
|
||||
|
||||
Component.onCompleted: refreshAuthDetection()
|
||||
Component.onCompleted: {
|
||||
refreshAuthDetection();
|
||||
refreshAuthServices();
|
||||
}
|
||||
onVisibleChanged: {
|
||||
if (visible)
|
||||
if (visible) {
|
||||
refreshAuthDetection();
|
||||
refreshAuthServices();
|
||||
}
|
||||
}
|
||||
|
||||
FileBrowserModal {
|
||||
@@ -64,6 +135,78 @@ Item {
|
||||
onFileSelected: path => SettingsData.set("lockScreenVideoPath", path)
|
||||
}
|
||||
|
||||
Process {
|
||||
id: authListServicesProcess
|
||||
command: ["dms", "auth", "list-services", "--json"]
|
||||
running: false
|
||||
|
||||
property string collected: ""
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: authListServicesProcess.collected = text || ""
|
||||
}
|
||||
|
||||
onExited: exitCode => {
|
||||
if (exitCode !== 0) {
|
||||
root.authServices = [];
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const data = JSON.parse(authListServicesProcess.collected);
|
||||
root.authServices = (data && Array.isArray(data.services)) ? data.services.filter(s => s.hasAuth) : [];
|
||||
} catch (e) {
|
||||
root.authServices = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: authValidateProcess
|
||||
running: false
|
||||
|
||||
property string collected: ""
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: authValidateProcess.collected = text || ""
|
||||
}
|
||||
|
||||
onExited: exitCode => {
|
||||
root.authValidateRunning = false;
|
||||
var data = null;
|
||||
try {
|
||||
data = JSON.parse(authValidateProcess.collected);
|
||||
} catch (e) {
|
||||
data = null;
|
||||
}
|
||||
if (!data) {
|
||||
root.authValidateOk = false;
|
||||
root.authValidateWarn = false;
|
||||
root.authValidateMessage = I18n.tr("Could not run validation. Ensure DMS is installed and dms is in PATH.");
|
||||
return;
|
||||
}
|
||||
if (data.valid) {
|
||||
SettingsData.set("lockPamPath", root.authPendingApplyPath);
|
||||
SettingsData.set("lockPamInlineFprint", data.inlineFingerprint === true);
|
||||
SettingsData.set("lockPamInlineU2f", data.inlineU2f === true);
|
||||
const warns = Array.isArray(data.warnings) ? data.warnings : [];
|
||||
root.authValidateOk = true;
|
||||
root.authValidateWarn = warns.length > 0;
|
||||
root.authValidateMessage = warns.length > 0 ? (I18n.tr("Applied with warnings:") + "\n" + warns.join("\n")) : I18n.tr("Applied successfully.");
|
||||
} else {
|
||||
root.authValidateOk = false;
|
||||
root.authValidateWarn = false;
|
||||
const errs = Array.isArray(data.errors) ? data.errors : [];
|
||||
const missing = Array.isArray(data.missingModules) ? data.missingModules : [];
|
||||
var msg = I18n.tr("Invalid — not applied.");
|
||||
if (errs.length > 0)
|
||||
msg = msg + "\n" + errs.join("\n");
|
||||
if (missing.length > 0)
|
||||
msg = msg + "\n" + I18n.tr("Missing modules: ") + missing.join(", ");
|
||||
root.authValidateMessage = msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankFlickable {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
@@ -207,6 +350,109 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
iconName: "key"
|
||||
title: I18n.tr("Authentication Source")
|
||||
settingKey: "lockAuthSource"
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Auto resolves the system auth stack automatically. Picking a source pins the lock screen to that PAM file.")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
width: parent.width
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
SettingsDropdownRow {
|
||||
settingKey: "lockPamPath"
|
||||
tags: ["lock", "screen", "pam", "authentication", "source", "service"]
|
||||
text: I18n.tr("Authentication Source")
|
||||
description: SettingsData.lockPamPath !== "" ? I18n.tr("Pinned to ") + SettingsData.lockPamPath : I18n.tr("Which PAM service the lock screen uses to authenticate")
|
||||
options: root.authOptions
|
||||
currentValue: root.authCurrentValue
|
||||
onValueChanged: value => {
|
||||
if (value === root.authAutoLabel) {
|
||||
root.authShowCustom = false;
|
||||
root.applyAutoAuthSource();
|
||||
return;
|
||||
}
|
||||
if (value === root.authCustomLabel) {
|
||||
root.authShowCustom = true;
|
||||
return;
|
||||
}
|
||||
root.authShowCustom = false;
|
||||
var svc = root.authServices.find(s => root.authServiceLabel(s) === value);
|
||||
if (svc)
|
||||
root.validateAndApplyAuthSource(svc.path);
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
visible: root.authShowCustom || root.authCurrentValue === root.authCustomLabel
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Enter the absolute path to a PAM service file, then validate and apply.")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
width: parent.width
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankTextField {
|
||||
id: customPamField
|
||||
width: parent.width - validatePamButton.width - Theme.spacingS
|
||||
placeholderText: "/etc/pam.d/my-service"
|
||||
text: SettingsData.lockPamPath
|
||||
backgroundColor: Theme.surfaceContainerHighest
|
||||
}
|
||||
|
||||
DankButton {
|
||||
id: validatePamButton
|
||||
text: I18n.tr("Validate & Apply")
|
||||
enabled: !root.authValidateRunning && customPamField.text.trim() !== ""
|
||||
onClicked: root.validateAndApplyAuthSource(customPamField.text.trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: Math.min(160, authStatusText.implicitHeight + Theme.spacingM * 2)
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHighest
|
||||
visible: root.authValidateMessage !== ""
|
||||
|
||||
StyledText {
|
||||
id: authStatusText
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
text: root.authValidateMessage
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: "monospace"
|
||||
color: !root.authValidateOk ? Theme.error : (root.authValidateWarn ? Theme.warning : Theme.surfaceVariantText)
|
||||
wrapMode: Text.Wrap
|
||||
verticalAlignment: Text.AlignTop
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: (SettingsData.lockPamInlineFprint && SettingsData.enableFprint) || (SettingsData.lockPamInlineU2f && SettingsData.enableU2f)
|
||||
text: I18n.tr("The chosen PAM stack already prompts for fingerprint or security key, so DMS's separate prompts are suppressed to avoid a double prompt.")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.warning
|
||||
width: parent.width
|
||||
wrapMode: Text.Wrap
|
||||
topPadding: Theme.spacingS
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
iconName: "lock"
|
||||
|
||||
@@ -4634,6 +4634,45 @@
|
||||
"zenbrowser"
|
||||
]
|
||||
},
|
||||
{
|
||||
"section": "lockAuthSource",
|
||||
"label": "Authentication Source",
|
||||
"tabIndex": 11,
|
||||
"category": "Lock Screen",
|
||||
"keywords": [
|
||||
"authentication",
|
||||
"lock",
|
||||
"login",
|
||||
"pam",
|
||||
"password",
|
||||
"pinned",
|
||||
"screen",
|
||||
"security",
|
||||
"service",
|
||||
"source"
|
||||
],
|
||||
"icon": "key",
|
||||
"description": "Pinned to "
|
||||
},
|
||||
{
|
||||
"section": "lockPamPath",
|
||||
"label": "Authentication Source",
|
||||
"tabIndex": 11,
|
||||
"category": "Lock Screen",
|
||||
"keywords": [
|
||||
"authentication",
|
||||
"lock",
|
||||
"login",
|
||||
"pam",
|
||||
"password",
|
||||
"pinned",
|
||||
"screen",
|
||||
"security",
|
||||
"service",
|
||||
"source"
|
||||
],
|
||||
"description": "Pinned to "
|
||||
},
|
||||
{
|
||||
"section": "lockScreenVideoCycling",
|
||||
"label": "Automatic Cycling",
|
||||
@@ -7954,14 +7993,57 @@
|
||||
"keywords": [
|
||||
"auth",
|
||||
"authentication",
|
||||
"block",
|
||||
"configure",
|
||||
"display manager",
|
||||
"existing",
|
||||
"external",
|
||||
"fingerprint",
|
||||
"fprintd",
|
||||
"greetd",
|
||||
"greeter",
|
||||
"login"
|
||||
"login",
|
||||
"managed",
|
||||
"never",
|
||||
"next",
|
||||
"pam",
|
||||
"removed",
|
||||
"security",
|
||||
"sync",
|
||||
"touches",
|
||||
"yourself"
|
||||
],
|
||||
"icon": "fingerprint"
|
||||
"icon": "fingerprint",
|
||||
"description": "DMS never touches /etc/pam.d/greetd; any existing DMS block is removed on next sync. Configure fingerprint and security-key auth yourself."
|
||||
},
|
||||
{
|
||||
"section": "greeterPamExternallyManaged",
|
||||
"label": "Manage greetd PAM automatically",
|
||||
"tabIndex": 31,
|
||||
"category": "Greeter",
|
||||
"keywords": [
|
||||
"auth",
|
||||
"automatically",
|
||||
"block",
|
||||
"configure",
|
||||
"display manager",
|
||||
"existing",
|
||||
"external",
|
||||
"fingerprint",
|
||||
"greetd",
|
||||
"greeter",
|
||||
"login",
|
||||
"manage",
|
||||
"managed",
|
||||
"never",
|
||||
"next",
|
||||
"pam",
|
||||
"removed",
|
||||
"security",
|
||||
"sync",
|
||||
"touches",
|
||||
"yourself"
|
||||
],
|
||||
"description": "DMS never touches /etc/pam.d/greetd; any existing DMS block is removed on next sync. Configure fingerprint and security-key auth yourself."
|
||||
},
|
||||
{
|
||||
"section": "greeterRememberLastSession",
|
||||
|
||||
Reference in New Issue
Block a user