diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 18a9413f0..47b87852d 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -2178,7 +2178,8 @@ "password": "User password", "anonymous-identity": "Anonymous Identity", "eap-method": "EAP Method", - "phase2-auth": "Phase 2 Authentication" + "phase2-auth": "Phase 2 Authentication", + "ca-cert": "CA Certificate" } } } diff --git a/Modules/Panels/Settings/Tabs/Connections/WifiSubTab.qml b/Modules/Panels/Settings/Tabs/Connections/WifiSubTab.qml index 49ee16407..444c1ea7a 100644 --- a/Modules/Panels/Settings/Tabs/Connections/WifiSubTab.qml +++ b/Modules/Panels/Settings/Tabs/Connections/WifiSubTab.qml @@ -25,6 +25,7 @@ Item { property string enterpriseEap: "peap" property string enterprisePhase2: "mschapv2" property string enterpriseAnonIdentity: "" + property string enterpriseCaCert: "" property string expandedSsid: "" property string infoSsid: "" property int ipVersion: 4 @@ -99,13 +100,15 @@ Item { enterpriseEap = "peap"; enterprisePhase2 = "mschapv2"; enterpriseAnonIdentity = ""; + enterpriseCaCert = ""; expandedSsid = ""; } function submitPassword(ssid, password, identity = "") { NetworkService.connect(ssid, password, false, identity, { eap: enterpriseEap, phase2: enterprisePhase2, - anonIdentity: enterpriseAnonIdentity + anonIdentity: enterpriseAnonIdentity, + caCert: enterpriseCaCert }); passwordSsid = ""; } @@ -372,6 +375,7 @@ Item { property string customEnterpriseEap: "peap" property string customEnterprisePhase2: "mschapv2" property string customEnterpriseAnonIdentity: "" + property string customEnterpriseCaCert: "" property bool customShowPassword: false property bool customIsHidden: false @@ -446,7 +450,8 @@ Item { NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, { eap: addNetworkPopup.customEnterpriseEap, phase2: addNetworkPopup.customEnterprisePhase2, - anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity + anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity, + caCert: addNetworkPopup.customEnterpriseCaCert }, addNetworkPopup.customIsHidden); addNetworkPopup.close(); } @@ -521,6 +526,28 @@ Item { onTextChanged: addNetworkPopup.customEnterpriseAnonIdentity = text } + RowLayout { + Layout.fillWidth: true + spacing: Style.marginM + visible: addNetworkPopup.customSecurityKey.indexOf("-eap") !== -1 + + NTextInput { + id: customCaCertInput + Layout.fillWidth: true + inputIconName: "certificate" + placeholderText: I18n.tr("wifi.enterprise.ca-cert") + label: I18n.tr("wifi.enterprise.ca-cert") + text: addNetworkPopup.customEnterpriseCaCert + onTextChanged: addNetworkPopup.customEnterpriseCaCert = text + } + + NIconButton { + icon: "folder-open" + Layout.alignment: Qt.AlignBottom + onClicked: caCertPicker.openForAddNetwork() + } + } + NTextInput { id: customIdentityInput Layout.fillWidth: true @@ -547,7 +574,8 @@ Item { NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, { eap: addNetworkPopup.customEnterpriseEap, phase2: addNetworkPopup.customEnterprisePhase2, - anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity + anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity, + caCert: addNetworkPopup.customEnterpriseCaCert }, addNetworkPopup.customIsHidden); addNetworkPopup.close(); } @@ -601,7 +629,8 @@ Item { NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, { eap: addNetworkPopup.customEnterpriseEap, phase2: addNetworkPopup.customEnterprisePhase2, - anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity + anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity, + caCert: addNetworkPopup.customEnterpriseCaCert }, addNetworkPopup.customIsHidden); addNetworkPopup.close(); } @@ -1177,6 +1206,48 @@ Item { currentKey: root.enterprisePhase2 onSelected: key => root.enterprisePhase2 = key } + + RowLayout { + Layout.fillWidth: true + spacing: Style.marginS + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: Style.baseWidgetSize * 0.9 + radius: Style.iRadiusXS + color: Color.mSurface + border.color: caCertInput.activeFocus ? Color.mSecondary : Color.mOutline + border.width: Style.borderS + + TextInput { + id: caCertInput + 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.enterpriseCaCert + onTextChanged: root.enterpriseCaCert = text + + NText { + visible: parent.text.length === 0 + anchors.verticalCenter: parent.verticalCenter + text: I18n.tr("wifi.enterprise.ca-cert") + color: Color.mOnSurfaceVariant + pointSize: Style.fontSizeS + } + } + } + + NIconButton { + icon: "folder-open" + baseSize: Style.baseWidgetSize * 0.8 + onClicked: caCertPicker.openForInline() + } + } } // Anonymous Identity field (Enterprise only) @@ -1370,4 +1441,32 @@ Item { } } } + + NFilePicker { + id: caCertPicker + title: I18n.tr("wifi.enterprise.ca-cert") + nameFilters: ["*.pem", "*.crt", "*.cer", "*.der", "*"] + + property bool isForAddNetwork: false + + function openForInline() { + isForAddNetwork = false; + openFilePicker(); + } + + function openForAddNetwork() { + isForAddNetwork = true; + openFilePicker(); + } + + onAccepted: paths => { + if (paths.length > 0) { + if (isForAddNetwork) { + addNetworkPopup.customEnterpriseCaCert = paths[0]; + } else { + root.enterpriseCaCert = paths[0]; + } + } + } + } } diff --git a/Services/Networking/NetworkService.qml b/Services/Networking/NetworkService.qml index 667145981..e32a781f4 100644 --- a/Services/Networking/NetworkService.qml +++ b/Services/Networking/NetworkService.qml @@ -390,6 +390,7 @@ Singleton { manualConnectProcess.eap = enterpriseConfig.eap || "peap"; manualConnectProcess.phase2 = enterpriseConfig.phase2 || "mschapv2"; manualConnectProcess.anonIdentity = enterpriseConfig.anonIdentity || ""; + manualConnectProcess.caCert = enterpriseConfig.caCert || ""; manualConnectProcess.running = true; } @@ -1475,46 +1476,40 @@ Singleton { property string eap: "peap" property string phase2: "mschapv2" property string anonIdentity: "" + property string caCert: "" property bool isHidden: false running: false command: { - var script = ` -SSID="$1" -PWD="$2" -SEC="$3" -IDENT="$4" -EAP_METHOD="\${5:-peap}" -PHASE2_AUTH="\${6:-mschapv2}" -ANON_IDENT="$7" -HIDDEN="\${8:-no}" + const nmArgs = ["connection", "add", "type", "wifi", "con-name", ssid, "ssid", ssid, "--", "802-11-wireless.hidden", isHidden ? "yes" : "no"]; -# Remove existing profile to avoid conflict -nmcli connection delete id "$SSID" 2>/dev/null || true + if (securityKey === "wpa-psk" || securityKey === "wpa2-psk") { + nmArgs.push("wifi-sec.key-mgmt", "wpa-psk", "wifi-sec.psk", password); + } else if (securityKey === "sae") { + nmArgs.push("wifi-sec.key-mgmt", "sae", "wifi-sec.psk", password); + } else if (securityKey === "wep") { + nmArgs.push("wifi-sec.key-mgmt", "none", "wifi-sec.wep-key0", password); + } else if (securityKey && securityKey.indexOf("-eap") !== -1) { + nmArgs.push("wifi-sec.key-mgmt", "wpa-eap", "802-1x.eap", eap, "802-1x.phase2-auth", phase2, "802-1x.identity", identity, "802-1x.password", password); -# 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 "$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 "$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 "$HIDDEN" -elif [[ "$SEC" == *-eap ]]; then - # 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 "$HIDDEN" -fi + if (anonIdentity) { + nmArgs.push("802-1x.anonymous-identity", anonIdentity); + } -# Bring up the connection -nmcli connection up id "$SSID" -`; + if (caCert) { + nmArgs.push("802-1x.ca-cert", caCert); + } + } - return ["sh", "-c", script, "--", ssid, password, securityKey, identity, eap, phase2, anonIdentity, isHidden ? "yes" : "no"]; + const script = ` + ID="$1" + nmcli connection delete id "$ID" 2>/dev/null || true + shift + nmcli "$@" + nmcli connection up id "$ID" + `; + + return ["sh", "-c", script, "--", ssid].concat(nmArgs); } stdout: StdioCollector {