1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-28 07:22:50 -05:00

net: fix VPN prompting for password

This commit is contained in:
bbedward
2025-11-21 12:59:12 -05:00
parent b310e66275
commit 4eb896629d
4 changed files with 181 additions and 100 deletions

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"strconv"
"time" "time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs"
@@ -125,8 +126,9 @@ func (a *SecretAgent) GetSecrets(
connType, displayName, vpnSvc := readConnTypeAndName(conn) connType, displayName, vpnSvc := readConnTypeAndName(conn)
ssid := readSSID(conn) ssid := readSSID(conn)
fields := fieldsNeeded(settingName, hints) fields := fieldsNeeded(settingName, hints)
vpnPasswordFlags := readVPNPasswordFlags(conn, settingName)
log.Infof("[SecretAgent] connType=%s, name=%s, vpnSvc=%s, fields=%v, flags=%d", connType, displayName, vpnSvc, fields, flags) log.Infof("[SecretAgent] connType=%s, name=%s, vpnSvc=%s, fields=%v, flags=%d, vpnPasswordFlags=%d", connType, displayName, vpnSvc, fields, flags, vpnPasswordFlags)
if a.backend != nil { if a.backend != nil {
a.backend.stateMutex.RLock() a.backend.stateMutex.RLock()
@@ -163,57 +165,70 @@ func (a *SecretAgent) GetSecrets(
} }
if len(fields) == 0 { if len(fields) == 0 {
// For VPN connections with no hints, we can't provide a proper UI.
// Defer to other agents (like nm-applet or VPN-specific auth dialogs)
// that can handle the VPN type properly (e.g., OpenConnect with SAML, etc.)
if settingName == "vpn" { if settingName == "vpn" {
log.Infof("[SecretAgent] VPN with empty hints - deferring to other agents for %s", vpnSvc) if a.backend != nil {
return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil) a.backend.stateMutex.RLock()
} isConnectingVPN := a.backend.state.IsConnectingVPN
a.backend.stateMutex.RUnlock()
const ( if !isConnectingVPN {
NM_SETTING_SECRET_FLAG_NONE = 0 log.Infof("[SecretAgent] VPN with empty hints - deferring to other agents for %s", vpnSvc)
NM_SETTING_SECRET_FLAG_AGENT_OWNED = 1 return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil)
NM_SETTING_SECRET_FLAG_NOT_SAVED = 2 }
NM_SETTING_SECRET_FLAG_NOT_REQUIRED = 4
)
var passwordFlags uint32 = 0xFFFF log.Infof("[SecretAgent] VPN with empty hints but we're connecting - prompting for password")
switch settingName { fields = []string{"password"}
case "802-11-wireless-security": } else {
if wifiSecSettings, ok := conn["802-11-wireless-security"]; ok { log.Infof("[SecretAgent] VPN with empty hints - deferring to other agents for %s", vpnSvc)
if flagsVariant, ok := wifiSecSettings["psk-flags"]; ok { return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil)
if pwdFlags, ok := flagsVariant.Value().(uint32); ok {
passwordFlags = pwdFlags
}
}
}
case "802-1x":
if dot1xSettings, ok := conn["802-1x"]; ok {
if flagsVariant, ok := dot1xSettings["password-flags"]; ok {
if pwdFlags, ok := flagsVariant.Value().(uint32); ok {
passwordFlags = pwdFlags
}
}
} }
} }
if passwordFlags == 0xFFFF { if len(fields) == 0 {
log.Warnf("[SecretAgent] Could not determine password-flags for empty hints - returning NoSecrets error") const (
return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil) NM_SETTING_SECRET_FLAG_NONE = 0
} else if passwordFlags&NM_SETTING_SECRET_FLAG_NOT_REQUIRED != 0 { NM_SETTING_SECRET_FLAG_AGENT_OWNED = 1
log.Infof("[SecretAgent] Secrets not required (flags=%d)", passwordFlags) NM_SETTING_SECRET_FLAG_NOT_SAVED = 2
out := nmSettingMap{} NM_SETTING_SECRET_FLAG_NOT_REQUIRED = 4
out[settingName] = nmVariantMap{} )
return out, nil
} else if passwordFlags&NM_SETTING_SECRET_FLAG_AGENT_OWNED != 0 { var passwordFlags uint32 = 0xFFFF
log.Warnf("[SecretAgent] Secrets are agent-owned but we don't store secrets (flags=%d) - returning NoSecrets error", passwordFlags) switch settingName {
return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil) case "802-11-wireless-security":
} else { if wifiSecSettings, ok := conn["802-11-wireless-security"]; ok {
log.Infof("[SecretAgent] No secrets needed, using system stored secrets (flags=%d)", passwordFlags) if flagsVariant, ok := wifiSecSettings["psk-flags"]; ok {
out := nmSettingMap{} if pwdFlags, ok := flagsVariant.Value().(uint32); ok {
out[settingName] = nmVariantMap{} passwordFlags = pwdFlags
return out, nil }
}
}
case "802-1x":
if dot1xSettings, ok := conn["802-1x"]; ok {
if flagsVariant, ok := dot1xSettings["password-flags"]; ok {
if pwdFlags, ok := flagsVariant.Value().(uint32); ok {
passwordFlags = pwdFlags
}
}
}
}
if passwordFlags == 0xFFFF {
log.Warnf("[SecretAgent] Could not determine password-flags for empty hints - returning NoSecrets error")
return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil)
} else if passwordFlags&NM_SETTING_SECRET_FLAG_NOT_REQUIRED != 0 {
log.Infof("[SecretAgent] Secrets not required (flags=%d)", passwordFlags)
out := nmSettingMap{}
out[settingName] = nmVariantMap{}
return out, nil
} else if passwordFlags&NM_SETTING_SECRET_FLAG_AGENT_OWNED != 0 {
log.Warnf("[SecretAgent] Secrets are agent-owned but we don't store secrets (flags=%d) - returning NoSecrets error", passwordFlags)
return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil)
} else {
log.Infof("[SecretAgent] No secrets needed, using system stored secrets (flags=%d)", passwordFlags)
out := nmSettingMap{}
out[settingName] = nmVariantMap{}
return out, nil
}
} }
} }
@@ -343,13 +358,11 @@ func (a *SecretAgent) GetSecrets(
// Update settings based on type // Update settings based on type
switch settingName { switch settingName {
case "vpn": case "vpn":
// Set password-flags=0 and add secrets to vpn section
vpn, ok := existingSettings["vpn"] vpn, ok := existingSettings["vpn"]
if !ok { if !ok {
vpn = make(map[string]dbus.Variant) vpn = make(map[string]dbus.Variant)
} }
// Get existing data map (vpn.data is string->string)
var data map[string]string var data map[string]string
if dataVariant, ok := vpn["data"]; ok { if dataVariant, ok := vpn["data"]; ok {
if dm, ok := dataVariant.Value().(map[string]string); ok { if dm, ok := dataVariant.Value().(map[string]string); ok {
@@ -364,11 +377,9 @@ func (a *SecretAgent) GetSecrets(
data = make(map[string]string) data = make(map[string]string)
} }
// Update password-flags to 0 (system-stored)
data["password-flags"] = "0" data["password-flags"] = "0"
vpn["data"] = dbus.MakeVariant(data) vpn["data"] = dbus.MakeVariant(data)
// Add secrets (vpn.secrets is string->string)
secs := make(map[string]string) secs := make(map[string]string)
for k, v := range reply.Secrets { for k, v := range reply.Secrets {
secs[k] = v secs[k] = v
@@ -379,14 +390,12 @@ func (a *SecretAgent) GetSecrets(
log.Infof("[SecretAgent] Updated VPN settings: password-flags=0, secrets with %d fields", len(secs)) log.Infof("[SecretAgent] Updated VPN settings: password-flags=0, secrets with %d fields", len(secs))
case "802-11-wireless-security": case "802-11-wireless-security":
// Set psk-flags=0 for WiFi
wifiSec, ok := existingSettings["802-11-wireless-security"] wifiSec, ok := existingSettings["802-11-wireless-security"]
if !ok { if !ok {
wifiSec = make(map[string]dbus.Variant) wifiSec = make(map[string]dbus.Variant)
} }
wifiSec["psk-flags"] = dbus.MakeVariant(uint32(0)) wifiSec["psk-flags"] = dbus.MakeVariant(uint32(0))
// Add PSK secret
if psk, ok := reply.Secrets["psk"]; ok { if psk, ok := reply.Secrets["psk"]; ok {
wifiSec["psk"] = dbus.MakeVariant(psk) wifiSec["psk"] = dbus.MakeVariant(psk)
log.Infof("[SecretAgent] Updated WiFi settings: psk-flags=0") log.Infof("[SecretAgent] Updated WiFi settings: psk-flags=0")
@@ -394,14 +403,12 @@ func (a *SecretAgent) GetSecrets(
settings["802-11-wireless-security"] = wifiSec settings["802-11-wireless-security"] = wifiSec
case "802-1x": case "802-1x":
// Set password-flags=0 for 802.1x
dot1x, ok := existingSettings["802-1x"] dot1x, ok := existingSettings["802-1x"]
if !ok { if !ok {
dot1x = make(map[string]dbus.Variant) dot1x = make(map[string]dbus.Variant)
} }
dot1x["password-flags"] = dbus.MakeVariant(uint32(0)) dot1x["password-flags"] = dbus.MakeVariant(uint32(0))
// Add password secret
if password, ok := reply.Secrets["password"]; ok { if password, ok := reply.Secrets["password"]; ok {
dot1x["password"] = dbus.MakeVariant(password) dot1x["password"] = dbus.MakeVariant(password)
log.Infof("[SecretAgent] Updated 802.1x settings: password-flags=0") log.Infof("[SecretAgent] Updated 802.1x settings: password-flags=0")
@@ -507,6 +514,39 @@ func fieldsNeeded(setting string, hints []string) []string {
} }
} }
func readVPNPasswordFlags(conn map[string]nmVariantMap, settingName string) uint32 {
if settingName != "vpn" {
return 0xFFFF
}
vpnSettings, ok := conn["vpn"]
if !ok {
return 0xFFFF
}
dataVariant, ok := vpnSettings["data"]
if !ok {
return 0xFFFF
}
dataMap, ok := dataVariant.Value().(map[string]string)
if !ok {
return 0xFFFF
}
flagsStr, ok := dataMap["password-flags"]
if !ok {
return 0xFFFF
}
flags64, err := strconv.ParseUint(flagsStr, 10, 32)
if err != nil {
return 0xFFFF
}
return uint32(flags64)
}
func reasonFromFlags(flags uint32) string { func reasonFromFlags(flags uint32) string {
const ( const (
NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE = 0x0 NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE = 0x0

View File

@@ -235,7 +235,7 @@ func (b *NetworkManagerBackend) ConnectVPN(uuidOrName string, singleActive bool)
} }
nm := b.nmConn.(gonetworkmanager.NetworkManager) nm := b.nmConn.(gonetworkmanager.NetworkManager)
activeConn, err := nm.ActivateConnection(targetConn, nil, nil) _, err = nm.ActivateConnection(targetConn, nil, nil)
if err != nil { if err != nil {
b.stateMutex.Lock() b.stateMutex.Lock()
b.state.IsConnectingVPN = false b.state.IsConnectingVPN = false
@@ -249,20 +249,6 @@ func (b *NetworkManagerBackend) ConnectVPN(uuidOrName string, singleActive bool)
return fmt.Errorf("failed to activate VPN: %w", err) return fmt.Errorf("failed to activate VPN: %w", err)
} }
if activeConn != nil {
state, _ := activeConn.GetPropertyState()
if state == 2 {
b.stateMutex.Lock()
b.state.IsConnectingVPN = false
b.state.ConnectingVPNUUID = ""
b.stateMutex.Unlock()
b.ListActiveVPN()
if b.onStateChange != nil {
b.onStateChange()
}
}
}
return nil return nil
} }

View File

@@ -10,6 +10,7 @@ DankModal {
id: root id: root
layerNamespace: "dms:wifi-password" layerNamespace: "dms:wifi-password"
keepPopoutsOpen: true
HyprlandFocusGrab { HyprlandFocusGrab {
windows: [root] windows: [root]
@@ -108,7 +109,11 @@ DankModal {
shouldBeVisible: false shouldBeVisible: false
width: 420 width: 420
height: requiresEnterprise ? 430 : 230 height: {
if (requiresEnterprise) return 430
if (isVpnPrompt) return 260
return 230
}
onShouldBeVisibleChanged: () => { onShouldBeVisibleChanged: () => {
if (!shouldBeVisible) { if (!shouldBeVisible) {
wifiPasswordInput = "" wifiPasswordInput = ""
@@ -321,7 +326,7 @@ DankModal {
if (passwordInput.text) secrets["password"] = passwordInput.text if (passwordInput.text) secrets["password"] = passwordInput.text
if (wifiAnonymousIdentityInput) secrets["anonymous-identity"] = wifiAnonymousIdentityInput if (wifiAnonymousIdentityInput) secrets["anonymous-identity"] = wifiAnonymousIdentityInput
} }
NetworkService.submitCredentials(promptToken, secrets, true) NetworkService.submitCredentials(promptToken, secrets, savePasswordCheckbox.checked)
} else { } else {
const username = requiresEnterprise ? usernameInput.text : "" const username = requiresEnterprise ? usernameInput.text : ""
NetworkService.connectToWifi( NetworkService.connectToWifi(
@@ -436,44 +441,91 @@ DankModal {
} }
} }
Row { Column {
spacing: Theme.spacingS spacing: Theme.spacingS
width: parent.width
Rectangle { Row {
id: showPasswordCheckbox spacing: Theme.spacingS
property bool checked: false Rectangle {
id: showPasswordCheckbox
width: 20 property bool checked: false
height: 20
radius: 4
color: checked ? Theme.primary : "transparent"
border.color: checked ? Theme.primary : Theme.outlineButton
border.width: 2
DankIcon { width: 20
anchors.centerIn: parent height: 20
name: "check" radius: 4
size: 12 color: checked ? Theme.primary : "transparent"
color: Theme.background border.color: checked ? Theme.primary : Theme.outlineButton
visible: parent.checked border.width: 2
DankIcon {
anchors.centerIn: parent
name: "check"
size: 12
color: Theme.background
visible: parent.checked
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: () => {
showPasswordCheckbox.checked = !showPasswordCheckbox.checked
}
}
} }
MouseArea { StyledText {
anchors.fill: parent text: I18n.tr("Show password")
hoverEnabled: true font.pixelSize: Theme.fontSizeMedium
cursorShape: Qt.PointingHandCursor color: Theme.surfaceText
onClicked: () => { anchors.verticalCenter: parent.verticalCenter
showPasswordCheckbox.checked = !showPasswordCheckbox.checked
}
} }
} }
StyledText { Row {
text: I18n.tr("Show password") spacing: Theme.spacingS
font.pixelSize: Theme.fontSizeMedium visible: isVpnPrompt
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter Rectangle {
id: savePasswordCheckbox
property bool checked: false
width: 20
height: 20
radius: 4
color: checked ? Theme.primary : "transparent"
border.color: checked ? Theme.primary : Theme.outlineButton
border.width: 2
DankIcon {
anchors.centerIn: parent
name: "check"
size: 12
color: Theme.background
visible: parent.checked
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: () => {
savePasswordCheckbox.checked = !savePasswordCheckbox.checked
}
}
}
StyledText {
text: I18n.tr("Save password")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
} }
} }
@@ -565,7 +617,7 @@ DankModal {
if (passwordInput.text) secrets["password"] = passwordInput.text if (passwordInput.text) secrets["password"] = passwordInput.text
if (wifiAnonymousIdentityInput) secrets["anonymous-identity"] = wifiAnonymousIdentityInput if (wifiAnonymousIdentityInput) secrets["anonymous-identity"] = wifiAnonymousIdentityInput
} }
NetworkService.submitCredentials(promptToken, secrets, true) NetworkService.submitCredentials(promptToken, secrets, savePasswordCheckbox.checked)
} else { } else {
const username = requiresEnterprise ? usernameInput.text : "" const username = requiresEnterprise ? usernameInput.text : ""
NetworkService.connectToWifi( NetworkService.connectToWifi(

View File

@@ -77,9 +77,12 @@ DankPopout {
screen: triggerScreen screen: triggerScreen
shouldBeVisible: false shouldBeVisible: false
property bool credentialsPromptOpen: NetworkService.credentialsRequested
WlrLayershell.keyboardFocus: { WlrLayershell.keyboardFocus: {
if (!shouldBeVisible) return WlrKeyboardFocus.None if (!shouldBeVisible) return WlrKeyboardFocus.None
if (powerMenuOpen) return WlrKeyboardFocus.None if (powerMenuOpen) return WlrKeyboardFocus.None
if (credentialsPromptOpen) return WlrKeyboardFocus.None
if (CompositorService.isHyprland) return WlrKeyboardFocus.OnDemand if (CompositorService.isHyprland) return WlrKeyboardFocus.OnDemand
return WlrKeyboardFocus.Exclusive return WlrKeyboardFocus.Exclusive
} }