diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 69b7da8a8..18a9413f0 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -2154,7 +2154,9 @@ "no-networks": "No Wi‑Fi networks found", "saved": "Saved", "searching": "Searching for networks...", - "add-hidden-network": "Add hidden network", + "add-network": "Add network", + "show-password": "Show password", + "hidden-network": "Hidden network", "network-name-ssid": "Network name (SSID)", "security-open": "None", "security-wpa": "WPA", @@ -2173,7 +2175,10 @@ }, "enterprise": { "username": "Identity / Username", - "password": "User password" + "password": "User password", + "anonymous-identity": "Anonymous Identity", + "eap-method": "EAP Method", + "phase2-auth": "Phase 2 Authentication" } } } diff --git a/Modules/Panels/Settings/Tabs/Connections/WifiSubTab.qml b/Modules/Panels/Settings/Tabs/Connections/WifiSubTab.qml index d7d6b9311..49ee16407 100644 --- a/Modules/Panels/Settings/Tabs/Connections/WifiSubTab.qml +++ b/Modules/Panels/Settings/Tabs/Connections/WifiSubTab.qml @@ -22,6 +22,9 @@ Item { // State properties property string passwordSsid: "" property string identity: "" + property string enterpriseEap: "peap" + property string enterprisePhase2: "mschapv2" + property string enterpriseAnonIdentity: "" property string expandedSsid: "" property string infoSsid: "" property int ipVersion: 4 @@ -93,10 +96,17 @@ Item { function requestPassword(ssid) { passwordSsid = ssid; identity = ""; + enterpriseEap = "peap"; + enterprisePhase2 = "mschapv2"; + enterpriseAnonIdentity = ""; expandedSsid = ""; } function submitPassword(ssid, password, identity = "") { - NetworkService.connect(ssid, password, false, identity); + NetworkService.connect(ssid, password, false, identity, { + eap: enterpriseEap, + phase2: enterprisePhase2, + anonIdentity: enterpriseAnonIdentity + }); passwordSsid = ""; } function cancelPassword() { @@ -291,7 +301,7 @@ Item { } NText { - text: I18n.tr("wifi.panel.add-hidden-network") + text: I18n.tr("wifi.panel.add-network") pointSize: Style.fontSizeM color: Color.mOnSurface Layout.fillWidth: true @@ -359,6 +369,11 @@ Item { property string customPassword: "" property string customIdentity: "" property string customSecurityKey: "wpa2-psk" + property string customEnterpriseEap: "peap" + property string customEnterprisePhase2: "mschapv2" + property string customEnterpriseAnonIdentity: "" + property bool customShowPassword: false + property bool customIsHidden: false onOpened: { customSsidInput.inputItem.forceActiveFocus(); @@ -407,7 +422,7 @@ Item { spacing: Style.marginXS NText { - text: I18n.tr("wifi.panel.add-hidden-network") + text: I18n.tr("wifi.panel.add-network") pointSize: Style.fontSizeL font.weight: Style.fontWeightBold color: Color.mOnSurface @@ -428,7 +443,11 @@ Item { onTextChanged: addNetworkPopup.customSsid = text onAccepted: { if (addNetworkPopup.customSsid.length > 0 && (addNetworkPopup.customSecurityKey === "open" || addNetworkPopup.customPassword.length > 0)) { - NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity); + NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, { + eap: addNetworkPopup.customEnterpriseEap, + phase2: addNetworkPopup.customEnterprisePhase2, + anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity + }, addNetworkPopup.customIsHidden); addNetworkPopup.close(); } } @@ -443,6 +462,65 @@ Item { } } + ColumnLayout { + visible: addNetworkPopup.customSecurityKey.indexOf("-eap") !== -1 + Layout.fillWidth: true + spacing: Style.marginM + + NComboBox { + Layout.fillWidth: true + label: I18n.tr("wifi.enterprise.eap-method") + model: [ + { + key: "peap", + name: "PEAP" + }, + { + key: "ttls", + name: "TTLS" + } + ] + currentKey: addNetworkPopup.customEnterpriseEap + onSelected: key => addNetworkPopup.customEnterpriseEap = key + } + + NComboBox { + Layout.fillWidth: true + label: I18n.tr("wifi.enterprise.phase2-auth") + model: [ + { + key: "mschapv2", + name: "MSCHAPv2" + }, + { + key: "pap", + name: "PAP" + }, + { + key: "mschap", + name: "MSCHAP" + }, + { + key: "chap", + name: "CHAP" + } + ] + currentKey: addNetworkPopup.customEnterprisePhase2 + onSelected: key => addNetworkPopup.customEnterprisePhase2 = key + } + } + + NTextInput { + id: customAnonIdentityInput + Layout.fillWidth: true + inputIconName: "user-question" + visible: addNetworkPopup.customSecurityKey.indexOf("-eap") !== -1 + placeholderText: I18n.tr("wifi.enterprise.anonymous-identity") + label: I18n.tr("wifi.enterprise.anonymous-identity") + text: addNetworkPopup.customEnterpriseAnonIdentity + onTextChanged: addNetworkPopup.customEnterpriseAnonIdentity = text + } + NTextInput { id: customIdentityInput Layout.fillWidth: true @@ -463,15 +541,39 @@ Item { label: I18n.tr("common.password") text: addNetworkPopup.customPassword onTextChanged: addNetworkPopup.customPassword = text - inputItem.echoMode: TextInput.Password + inputItem.echoMode: addNetworkPopup.customShowPassword ? TextInput.Normal : TextInput.Password onAccepted: { if (addNetworkPopup.customSsid.length > 0 && addNetworkPopup.customPassword.length > 0) { - NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity); + NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, { + eap: addNetworkPopup.customEnterpriseEap, + phase2: addNetworkPopup.customEnterprisePhase2, + anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity + }, addNetworkPopup.customIsHidden); addNetworkPopup.close(); } } } + RowLayout { + Layout.fillWidth: true + spacing: Style.marginM + + NCheckbox { + Layout.fillWidth: true + label: I18n.tr("wifi.panel.show-password") + checked: addNetworkPopup.customShowPassword + onToggled: checked => addNetworkPopup.customShowPassword = checked + visible: addNetworkPopup.customSecurityKey !== "open" + } + + NCheckbox { + Layout.fillWidth: true + label: I18n.tr("wifi.panel.hidden-network") + checked: addNetworkPopup.customIsHidden + onToggled: checked => addNetworkPopup.customIsHidden = checked + } + } + // Actions RowLayout { Layout.fillWidth: true @@ -496,7 +598,11 @@ Item { textColor: Color.mOnPrimary enabled: addNetworkPopup.customSsid.length > 0 && (addNetworkPopup.customSecurityKey === "open" || addNetworkPopup.customPassword.length > 0) && (addNetworkPopup.customSecurityKey.indexOf("-eap") === -1 || addNetworkPopup.customIdentity.length > 0) onClicked: { - NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity); + NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, { + eap: addNetworkPopup.customEnterpriseEap, + phase2: addNetworkPopup.customEnterprisePhase2, + anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity + }, addNetworkPopup.customIsHidden); addNetworkPopup.close(); } } @@ -1024,6 +1130,89 @@ Item { Layout.fillWidth: true spacing: Style.marginS + // Enterprise Configuration + ColumnLayout { + visible: networkItem.isEnterprise + Layout.fillWidth: true + spacing: Style.marginS + + NComboBox { + Layout.fillWidth: true + label: I18n.tr("wifi.enterprise.eap-method") + model: [ + { + key: "peap", + name: "PEAP" + }, + { + key: "ttls", + name: "TTLS" + } + ] + currentKey: root.enterpriseEap + onSelected: key => root.enterpriseEap = key + } + + NComboBox { + Layout.fillWidth: true + label: I18n.tr("wifi.enterprise.phase2-auth") + model: [ + { + key: "mschapv2", + name: "MSCHAPv2" + }, + { + key: "pap", + name: "PAP" + }, + { + key: "mschap", + name: "MSCHAP" + }, + { + key: "chap", + name: "CHAP" + } + ] + currentKey: root.enterprisePhase2 + onSelected: key => root.enterprisePhase2 = key + } + } + + // Anonymous Identity field (Enterprise only) + Rectangle { + visible: networkItem.isEnterprise + Layout.fillWidth: true + Layout.preferredHeight: Style.baseWidgetSize * 0.9 + radius: Style.iRadiusXS + color: Color.mSurface + border.color: anonIdentityInput.activeFocus ? Color.mSecondary : Color.mOutline + border.width: Style.borderS + + TextInput { + id: anonIdentityInput + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Style.marginS + font.family: Settings.data.ui.fontFixed + font.pointSize: Style.fontSizeS + color: Color.mOnSurface + selectByMouse: true + text: root.enterpriseAnonIdentity + onTextChanged: root.enterpriseAnonIdentity = text + onAccepted: identityInput.forceActiveFocus() + + NText { + visible: parent.text.length === 0 + anchors.verticalCenter: parent.verticalCenter + text: I18n.tr("wifi.enterprise.anonymous-identity") + color: Color.mOnSurfaceVariant + pointSize: Style.fontSizeS + } + } + } + // Identity field (Enterprise only) Rectangle { visible: networkItem.isEnterprise diff --git a/Services/Networking/NetworkService.qml b/Services/Networking/NetworkService.qml index b4c75e791..667145981 100644 --- a/Services/Networking/NetworkService.qml +++ b/Services/Networking/NetworkService.qml @@ -342,7 +342,7 @@ Singleton { refreshActiveEthernetDetails(); } - function connect(ssid, password = "", isHidden = false, identity = "") { + function connect(ssid, password = "", isHidden = false, identity = "", enterpriseConfig = {}) { if (!ProgramCheckerService.nmcliAvailable || connecting) { return; } @@ -350,7 +350,7 @@ Singleton { // For enterprise networks, use the robust manual connection logic (profile creation) // as it handles 802.1X parameters much more reliably than 'device wifi connect'. if (isEnterprise(networks[ssid] ? networks[ssid].security : "")) { - connectManual(ssid, password, "wpa-eap", identity); + connectManual(ssid, password, "wpa-eap", identity, enterpriseConfig); return; } @@ -374,7 +374,7 @@ Singleton { connectProcess.running = true; } - function connectManual(ssid, password, securityKey, identity = "") { + function connectManual(ssid, password, securityKey, identity = "", enterpriseConfig = {}, isHidden = false) { if (!ProgramCheckerService.nmcliAvailable || connecting) { return; } @@ -386,6 +386,10 @@ Singleton { manualConnectProcess.password = password; manualConnectProcess.securityKey = securityKey; manualConnectProcess.identity = identity; + manualConnectProcess.isHidden = isHidden; + manualConnectProcess.eap = enterpriseConfig.eap || "peap"; + manualConnectProcess.phase2 = enterpriseConfig.phase2 || "mschapv2"; + manualConnectProcess.anonIdentity = enterpriseConfig.anonIdentity || ""; manualConnectProcess.running = true; } @@ -1468,6 +1472,10 @@ Singleton { property string password: "" property string securityKey: "" property string identity: "" + property string eap: "peap" + property string phase2: "mschapv2" + property string anonIdentity: "" + property bool isHidden: false running: false command: { @@ -1476,29 +1484,37 @@ SSID="$1" PWD="$2" SEC="$3" IDENT="$4" +EAP_METHOD="\${5:-peap}" +PHASE2_AUTH="\${6:-mschapv2}" +ANON_IDENT="$7" +HIDDEN="\${8:-no}" # Remove existing profile to avoid conflict nmcli connection delete id "$SSID" 2>/dev/null || true # Create profile based on security key if [ "$SEC" = "wpa-psk" ] || [ "$SEC" = "wpa2-psk" ]; then - nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- wifi-sec.key-mgmt wpa-psk wifi-sec.psk "$PWD" 802-11-wireless.hidden yes + nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- wifi-sec.key-mgmt wpa-psk wifi-sec.psk "$PWD" 802-11-wireless.hidden "$HIDDEN" elif [ "$SEC" = "sae" ]; then - nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- wifi-sec.key-mgmt sae wifi-sec.psk "$PWD" 802-11-wireless.hidden yes + nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- wifi-sec.key-mgmt sae wifi-sec.psk "$PWD" 802-11-wireless.hidden "$HIDDEN" elif [ "$SEC" = "wep" ]; then - nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- wifi-sec.key-mgmt none wifi-sec.wep-key0 "$PWD" 802-11-wireless.hidden yes + nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- wifi-sec.key-mgmt none wifi-sec.wep-key0 "$PWD" 802-11-wireless.hidden "$HIDDEN" elif [[ "$SEC" == *-eap ]]; then - # WPA Enterprise (PEAP-MSCHAPv2 default) - nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- wifi-sec.key-mgmt wpa-eap 802-1x.eap peap 802-1x.phase2-auth mschapv2 802-1x.identity "$IDENT" 802-1x.password "$PWD" 802-11-wireless.hidden yes + # WPA Enterprise + if [ -n "$ANON_IDENT" ]; then + nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- wifi-sec.key-mgmt wpa-eap 802-1x.eap "$EAP_METHOD" 802-1x.phase2-auth "$PHASE2_AUTH" 802-1x.identity "$IDENT" 802-1x.password "$PWD" 802-1x.anonymous-identity "$ANON_IDENT" 802-11-wireless.hidden "$HIDDEN" + else + nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- wifi-sec.key-mgmt wpa-eap 802-1x.eap "$EAP_METHOD" 802-1x.phase2-auth "$PHASE2_AUTH" 802-1x.identity "$IDENT" 802-1x.password "$PWD" 802-11-wireless.hidden "$HIDDEN" + fi else - nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- 802-11-wireless.hidden yes + nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- 802-11-wireless.hidden "$HIDDEN" fi # Bring up the connection nmcli connection up id "$SSID" `; - return ["sh", "-c", script, "--", ssid, password, securityKey, identity]; + return ["sh", "-c", script, "--", ssid, password, securityKey, identity, eap, phase2, anonIdentity, isHidden ? "yes" : "no"]; } stdout: StdioCollector {