SEBWIN-788: Implemented automatic connection attempt and retry on invalid credentials. Improved wording of username label in credentials dialog.

This commit is contained in:
Damian Büchel 2024-06-03 19:41:54 +02:00
parent b3228aedef
commit 84bbcb82ef
12 changed files with 99 additions and 61 deletions

View file

@ -106,7 +106,7 @@
Benutzername: Benutzername:
</Entry> </Entry>
<Entry key="CredentialsDialog_UsernameOptionalLabel"> <Entry key="CredentialsDialog_UsernameOptionalLabel">
Benutzername (optional): Benutzername (falls erforderlich):
</Entry> </Entry>
<Entry key="CredentialsDialog_WirelessNetworkMessage"> <Entry key="CredentialsDialog_WirelessNetworkMessage">
Bitte geben Sie die erforderlichen Anmeldeinformationen ein, um eine Verbindung mit dem drahtlosen Netzwerk "%%_NAME_%%" herzustellen. Bitte geben Sie die erforderlichen Anmeldeinformationen ein, um eine Verbindung mit dem drahtlosen Netzwerk "%%_NAME_%%" herzustellen.

View file

@ -106,7 +106,7 @@
Username: Username:
</Entry> </Entry>
<Entry key="CredentialsDialog_UsernameOptionalLabel"> <Entry key="CredentialsDialog_UsernameOptionalLabel">
Username (optional): Username (if required):
</Entry> </Entry>
<Entry key="CredentialsDialog_WirelessNetworkMessage"> <Entry key="CredentialsDialog_WirelessNetworkMessage">
Please enter the required credentials to establish a connection to the wireless network "%%_NAME_%%". Please enter the required credentials to establish a connection to the wireless network "%%_NAME_%%".

View file

@ -106,7 +106,7 @@
Nombre de usuario: Nombre de usuario:
</Entry> </Entry>
<Entry key="CredentialsDialog_UsernameOptionalLabel"> <Entry key="CredentialsDialog_UsernameOptionalLabel">
Nombre de usuario (opcional): Nombre de usuario (si se requiere):
</Entry> </Entry>
<Entry key="CredentialsDialog_WirelessNetworkMessage"> <Entry key="CredentialsDialog_WirelessNetworkMessage">
Por favor, introduzca las credenciales necesarias para establecer una conexión con la red inalámbrica "%%_NAME_%%". Por favor, introduzca las credenciales necesarias para establecer una conexión con la red inalámbrica "%%_NAME_%%".

View file

@ -106,7 +106,7 @@
Kasutajanimi: Kasutajanimi:
</Entry> </Entry>
<Entry key="CredentialsDialog_UsernameOptionalLabel"> <Entry key="CredentialsDialog_UsernameOptionalLabel">
Kasutajanimi (vabatahtlik): Kasutajanimi (kui see on nõutav):
</Entry> </Entry>
<Entry key="CredentialsDialog_WirelessNetworkMessage"> <Entry key="CredentialsDialog_WirelessNetworkMessage">
Palun sisestage vajalikud volitused, et luua ühendus traadita võrguga "%%_NAME_%%". Palun sisestage vajalikud volitused, et luua ühendus traadita võrguga "%%_NAME_%%".

View file

@ -106,7 +106,7 @@
Nom d'utilisateur: Nom d'utilisateur:
</Entry> </Entry>
<Entry key="CredentialsDialog_UsernameOptionalLabel"> <Entry key="CredentialsDialog_UsernameOptionalLabel">
Nom d'utilisateur (facultatif): Nom d'utilisateur (si requis):
</Entry> </Entry>
<Entry key="CredentialsDialog_WirelessNetworkMessage"> <Entry key="CredentialsDialog_WirelessNetworkMessage">
Veuillez saisir les informations d'identification requises pour établir une connexion au réseau sans fil "%%_NAME_%%". Veuillez saisir les informations d'identification requises pour établir une connexion au réseau sans fil "%%_NAME_%%".

View file

@ -106,7 +106,7 @@
Nama pengguna: Nama pengguna:
</Entry> </Entry>
<Entry key="CredentialsDialog_UsernameOptionalLabel"> <Entry key="CredentialsDialog_UsernameOptionalLabel">
Nama pengguna (opsional): Nama pengguna (jika diperlukan):
</Entry> </Entry>
<Entry key="CredentialsDialog_WirelessNetworkMessage"> <Entry key="CredentialsDialog_WirelessNetworkMessage">
Masukkan kredensial yang diperlukan untuk membuat sambungan ke jaringan nirkabel "%%_NAMA_%%". Masukkan kredensial yang diperlukan untuk membuat sambungan ke jaringan nirkabel "%%_NAMA_%%".

View file

@ -106,7 +106,7 @@
Nome utente: Nome utente:
</Entry> </Entry>
<Entry key="CredentialsDialog_UsernameOptionalLabel"> <Entry key="CredentialsDialog_UsernameOptionalLabel">
Nome utente (facoltativo): Nome utente (se richiesto):
</Entry> </Entry>
<Entry key="CredentialsDialog_WirelessNetworkMessage"> <Entry key="CredentialsDialog_WirelessNetworkMessage">
Inserire le credenziali richieste per stabilire una connessione alla rete wireless "%%_NAME_%%". Inserire le credenziali richieste per stabilire una connessione alla rete wireless "%%_NAME_%%".

View file

@ -106,7 +106,7 @@
Gebruikersnaam: Gebruikersnaam:
</Entry> </Entry>
<Entry key="CredentialsDialog_UsernameOptionalLabel"> <Entry key="CredentialsDialog_UsernameOptionalLabel">
Gebruikersnaam (optioneel): Gebruikersnaam (indien vereist):
</Entry> </Entry>
<Entry key="CredentialsDialog_WirelessNetworkMessage"> <Entry key="CredentialsDialog_WirelessNetworkMessage">
Voer de vereiste gegevens in om verbinding te maken met het draadloze netwerk “%%_NAME_%%”. Voer de vereiste gegevens in om verbinding te maken met het draadloze netwerk “%%_NAME_%%”.

View file

@ -106,7 +106,7 @@
Имя пользователя: Имя пользователя:
</Entry> </Entry>
<Entry key="CredentialsDialog_UsernameOptionalLabel"> <Entry key="CredentialsDialog_UsernameOptionalLabel">
Имя пользователя (необязательно): Имя пользователя (если требуется):
</Entry> </Entry>
<Entry key="CredentialsDialog_WirelessNetworkMessage"> <Entry key="CredentialsDialog_WirelessNetworkMessage">
Введите необходимые учетные данные для установления соединения с беспроводной сетью "%%_NAME_%%". Введите необходимые учетные данные для установления соединения с беспроводной сетью "%%_NAME_%%".

View file

@ -106,7 +106,7 @@
用户名: 用户名:
</Entry> </Entry>
<Entry key="CredentialsDialog_UsernameOptionalLabel"> <Entry key="CredentialsDialog_UsernameOptionalLabel">
用户名(可选 用户名(如需要
</Entry> </Entry>
<Entry key="CredentialsDialog_WirelessNetworkMessage"> <Entry key="CredentialsDialog_WirelessNetworkMessage">
请输入与无线网络 "%%_NAME_%%" 建立连接所需的凭证。 请输入与无线网络 "%%_NAME_%%" 建立连接所需的凭证。

View file

@ -11,6 +11,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using SafeExamBrowser.SystemComponents.Contracts.Network; using SafeExamBrowser.SystemComponents.Contracts.Network;
using Windows.Devices.WiFi; using Windows.Devices.WiFi;
using Windows.Networking.Connectivity;
namespace SafeExamBrowser.SystemComponents.Network namespace SafeExamBrowser.SystemComponents.Network
{ {
@ -21,6 +22,16 @@ namespace SafeExamBrowser.SystemComponents.Network
return networks.Where(n => !string.IsNullOrEmpty(n.Ssid)).GroupBy(n => n.Ssid).Select(g => g.First()).OrderBy(n => n.Ssid); return networks.Where(n => !string.IsNullOrEmpty(n.Ssid)).GroupBy(n => n.Ssid).Select(g => g.First()).OrderBy(n => n.Ssid);
} }
internal static bool IsOpen(this WiFiAvailableNetwork network)
{
return network.SecuritySettings.NetworkAuthenticationType == NetworkAuthenticationType.Open80211 && network.SecuritySettings.NetworkEncryptionType == NetworkEncryptionType.None;
}
internal static string ToLogString(this WiFiAvailableNetwork network)
{
return $"'{network.Ssid}' ({network.SecuritySettings.NetworkAuthenticationType}, {network.SecuritySettings.NetworkEncryptionType})";
}
internal static WirelessNetwork ToWirelessNetwork(this WiFiAvailableNetwork network) internal static WirelessNetwork ToWirelessNetwork(this WiFiAvailableNetwork network)
{ {
return new WirelessNetwork return new WirelessNetwork

View file

@ -7,9 +7,11 @@
*/ */
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Threading.Tasks;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Network; using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.SystemComponents.Contracts.Network.Events; using SafeExamBrowser.SystemComponents.Contracts.Network.Events;
@ -27,12 +29,13 @@ namespace SafeExamBrowser.SystemComponents.Network
{ {
private readonly object @lock = new object(); private readonly object @lock = new object();
private readonly ConcurrentDictionary<string, object> attempts;
private readonly ILogger logger; private readonly ILogger logger;
private readonly INativeMethods nativeMethods; private readonly INativeMethods nativeMethods;
private readonly List<WirelessNetwork> wirelessNetworks; private readonly List<WirelessNetwork> wirelessNetworks;
private Timer timer;
private WiFiAdapter adapter; private WiFiAdapter adapter;
private Timer timer;
private bool HasWirelessAdapter => adapter != default; private bool HasWirelessAdapter => adapter != default;
@ -44,6 +47,7 @@ namespace SafeExamBrowser.SystemComponents.Network
public NetworkAdapter(ILogger logger, INativeMethods nativeMethods) public NetworkAdapter(ILogger logger, INativeMethods nativeMethods)
{ {
this.attempts = new ConcurrentDictionary<string, object>();
this.logger = logger; this.logger = logger;
this.nativeMethods = nativeMethods; this.nativeMethods = nativeMethods;
this.wirelessNetworks = new List<WirelessNetwork>(); this.wirelessNetworks = new List<WirelessNetwork>();
@ -51,6 +55,7 @@ namespace SafeExamBrowser.SystemComponents.Network
public void ConnectToWirelessNetwork(string name) public void ConnectToWirelessNetwork(string name)
{ {
var isFirstAttempt = !attempts.TryGetValue(name, out _);
var network = default(WiFiAvailableNetwork); var network = default(WiFiAvailableNetwork);
lock (@lock) lock (@lock)
@ -60,29 +65,21 @@ namespace SafeExamBrowser.SystemComponents.Network
if (network != default) if (network != default)
{ {
var isOpen = network.SecuritySettings.NetworkAuthenticationType == NetworkAuthenticationType.Open80211 && network.SecuritySettings.NetworkEncryptionType == NetworkEncryptionType.None; if (isFirstAttempt || network.IsOpen())
if (isOpen)
{ {
logger.Info($"Attempting to connect to open wireless network '{name}'..."); ConnectAutomatically(network);
adapter.ConnectAsync(network, WiFiReconnectionKind.Automatic).Completed = (o, s) => Adapter_ConnectCompleted(name, o, s);
Status = ConnectionStatus.Connecting;
} }
else if (TryGetCredentials(name, out var credentials)) else
{ {
logger.Info($"Attempting to connect to wireless network '{name}' with credentials..."); ConnectWithAuthentication(network);
adapter.ConnectAsync(network, WiFiReconnectionKind.Automatic, credentials).Completed = (o, s) => Adapter_ConnectCompleted(name, o, s);
Status = ConnectionStatus.Connecting;
} }
Changed?.Invoke();
} }
else else
{ {
logger.Warn($"Could not find wireless network '{name}'!"); logger.Warn($"Could not find wireless network '{name}'!");
} }
Changed?.Invoke();
} }
public IEnumerable<IWirelessNetwork> GetWirelessNetworks() public IEnumerable<IWirelessNetwork> GetWirelessNetworks()
@ -146,6 +143,68 @@ namespace SafeExamBrowser.SystemComponents.Network
logger.Info("Stopped monitoring the network adapter."); logger.Info("Stopped monitoring the network adapter.");
} }
private void Adapter_AvailableNetworksChanged(WiFiAdapter sender, object args)
{
Update(false);
}
private void Adapter_ConnectCompleted(WiFiAvailableNetwork network, IAsyncOperation<WiFiConnectionResult> operation, AsyncStatus status)
{
var connectionStatus = default(WiFiConnectionStatus?);
if (status == AsyncStatus.Completed)
{
connectionStatus = operation.GetResults()?.ConnectionStatus;
}
else
{
logger.Error($"Failed to complete connection operation! Status: {status}.");
}
if (connectionStatus == WiFiConnectionStatus.Success)
{
attempts.TryRemove(network.Ssid, out _);
logger.Info($"Successfully connected to wireless network {network.ToLogString()}.");
}
else if (connectionStatus == WiFiConnectionStatus.InvalidCredential)
{
attempts.TryAdd(network.Ssid, default);
logger.Info($"Failed to connect to wireless network {network.ToLogString()} due to invalid credentials. Retrying...");
Task.Run(() => ConnectToWirelessNetwork(network.Ssid));
}
else
{
Status = ConnectionStatus.Disconnected;
logger.Error($"Failed to connect to wireless network {network.ToLogString()}! Reason: {connectionStatus}.");
}
Update();
}
private void ConnectAutomatically(WiFiAvailableNetwork network)
{
logger.Info($"Attempting to automatically connect to {(network.IsOpen() ? "open" : "protected")} wireless network {network.ToLogString()}...");
adapter.ConnectAsync(network, WiFiReconnectionKind.Automatic).Completed = (o, s) => Adapter_ConnectCompleted(network, o, s);
Status = ConnectionStatus.Connecting;
}
private void ConnectWithAuthentication(WiFiAvailableNetwork network)
{
if (TryGetCredentials(network.Ssid, out var credentials))
{
logger.Info($"Attempting to connect to protected wirless network {network.ToLogString()}...");
adapter.ConnectAsync(network, WiFiReconnectionKind.Automatic, credentials).Completed = (o, s) => Adapter_ConnectCompleted(network, o, s);
Status = ConnectionStatus.Connecting;
}
else
{
Status = ConnectionStatus.Disconnected;
Update();
}
}
private void InitializeAdapter() private void InitializeAdapter()
{ {
try try
@ -188,38 +247,6 @@ namespace SafeExamBrowser.SystemComponents.Network
} }
} }
private void Adapter_AvailableNetworksChanged(WiFiAdapter sender, object args)
{
Update(false);
}
private void Adapter_ConnectCompleted(string name, IAsyncOperation<WiFiConnectionResult> operation, AsyncStatus status)
{
var connectionStatus = default(WiFiConnectionStatus?);
if (status == AsyncStatus.Completed)
{
connectionStatus = operation.GetResults()?.ConnectionStatus;
}
else
{
logger.Error($"Failed to complete connection operation! Status: {status}.");
}
if (connectionStatus == WiFiConnectionStatus.Success)
{
Status = ConnectionStatus.Connected;
logger.Info($"Successfully connected to wireless network '{name}'.");
}
else
{
Status = ConnectionStatus.Disconnected;
logger.Error($"Failed to connect to wireless network '{name}'! Reason: {connectionStatus}.");
}
Update();
}
private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e) private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
{ {
logger.Debug("Network address changed."); logger.Debug("Network address changed.");
@ -292,7 +319,7 @@ namespace SafeExamBrowser.SystemComponents.Network
{ {
lock (@lock) lock (@lock)
{ {
var hasInternet = nativeMethods.HasInternetConnection(); var hasConnection = nativeMethods.HasInternetConnection();
var isConnecting = Status == ConnectionStatus.Connecting; var isConnecting = Status == ConnectionStatus.Connecting;
var previousStatus = Status; var previousStatus = Status;
@ -300,7 +327,7 @@ namespace SafeExamBrowser.SystemComponents.Network
if (HasWirelessAdapter) if (HasWirelessAdapter)
{ {
TryGetCurrentWirelessNetwork(out var currentNetwork); hasConnection &= TryGetCurrentWirelessNetwork(out var currentNetwork);
foreach (var network in adapter.NetworkReport.AvailableNetworks.FilterAndOrder()) foreach (var network in adapter.NetworkReport.AvailableNetworks.FilterAndOrder())
{ {
@ -320,8 +347,8 @@ namespace SafeExamBrowser.SystemComponents.Network
} }
} }
Type = HasWirelessAdapter ? ConnectionType.Wireless : (hasInternet ? ConnectionType.Wired : ConnectionType.Undefined); Type = HasWirelessAdapter ? ConnectionType.Wireless : (hasConnection ? ConnectionType.Wired : ConnectionType.Undefined);
Status = hasInternet ? ConnectionStatus.Connected : (HasWirelessAdapter && isConnecting ? ConnectionStatus.Connecting : ConnectionStatus.Disconnected); Status = hasConnection ? ConnectionStatus.Connected : (isConnecting ? ConnectionStatus.Connecting : ConnectionStatus.Disconnected);
LogNetworkChanges(previousStatus, wirelessNetworks.FirstOrDefault(n => n.Status == ConnectionStatus.Connected)); LogNetworkChanges(previousStatus, wirelessNetworks.FirstOrDefault(n => n.Status == ConnectionStatus.Connected));
} }