Skip to content

Commit 721b5bd

Browse files
Feature: Prompt for credentials when connecting smb (#11144)
1 parent 537a0ed commit 721b5bd

File tree

5 files changed

+171
-2
lines changed

5 files changed

+171
-2
lines changed

src/Files.App/Filesystem/NetworkDrivesAPI.cs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
using Files.App.Shell;
1+
using Files.App.Extensions;
2+
using Files.App.Helpers;
3+
using Files.App.Shell;
24
using System;
35
using System.ComponentModel;
46
using System.Runtime.InteropServices;
7+
using System.Text;
58
using System.Threading.Tasks;
69
using System.Windows.Forms;
710
using Vanara.Extensions;
811
using Vanara.InteropServices;
12+
using static Vanara.PInvoke.AdvApi32;
13+
using Vanara.PInvoke;
914
using static Vanara.PInvoke.Mpr;
1015

1116
namespace Files.App.Filesystem
@@ -117,6 +122,52 @@ public static Task<bool> OpenMapNetworkDriveDialog(long hwnd)
117122
});
118123
}
119124

125+
public static async Task<bool> AuthenticateNetworkShare(string path)
126+
{
127+
NETRESOURCE nr = new NETRESOURCE
128+
{
129+
dwType = NETRESOURCEType.RESOURCETYPE_DISK,
130+
lpRemoteName = path
131+
};
132+
133+
Win32Error connectionError = WNetAddConnection3(HWND.NULL, nr, null, null, 0); // if creds are saved, this will return NO_ERROR
134+
135+
if (connectionError == Win32Error.ERROR_LOGON_FAILURE)
136+
{
137+
var dialog = DynamicDialogFactory.GetFor_CredentialEntryDialog(path);
138+
await dialog.ShowAsync();
139+
var credentialsReturned = dialog.ViewModel.AdditionalData as string[];
140+
141+
if (credentialsReturned is string[] && credentialsReturned[1] != null)
142+
{
143+
connectionError = WNetAddConnection3(HWND.NULL, nr, credentialsReturned[1], credentialsReturned[0], 0);
144+
if (credentialsReturned[2] == "y" && connectionError == Win32Error.NO_ERROR)
145+
{
146+
CREDENTIAL creds = new CREDENTIAL();
147+
creds.TargetName = new StrPtrAuto(path.Substring(2));
148+
creds.UserName = new StrPtrAuto(credentialsReturned[0]);
149+
creds.Type = CRED_TYPE.CRED_TYPE_DOMAIN_PASSWORD;
150+
creds.AttributeCount = 0;
151+
creds.Persist = CRED_PERSIST.CRED_PERSIST_ENTERPRISE;
152+
byte[] bpassword = Encoding.Unicode.GetBytes(credentialsReturned[1]);
153+
creds.CredentialBlobSize = (UInt32)bpassword.Length;
154+
creds.CredentialBlob = Marshal.StringToCoTaskMemUni(credentialsReturned[1]);
155+
CredWrite(creds, 0);
156+
}
157+
}
158+
else
159+
return false;
160+
}
161+
162+
if (connectionError == Win32Error.NO_ERROR)
163+
return true;
164+
else
165+
{
166+
await DialogDisplayHelper.ShowDialogAsync("NetworkFolderErrorDialogTitle".GetLocalizedResource(), connectionError.ToString().Split(":")[1].Trim());
167+
return false;
168+
}
169+
}
170+
120171
public static bool DisconnectNetworkDrive(string drive)
121172
{
122173
return WNetCancelConnection2(drive.TrimEnd('\\'), CONNECT.CONNECT_UPDATE_PROFILE, true).Succeeded;

src/Files.App/Helpers/DynamicDialogFactory.cs

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ public static DynamicDialog GetFor_RenameDialog()
6060
DynamicDialog? dialog = null;
6161
TextBox inputText = new()
6262
{
63-
Height = 35d,
6463
PlaceholderText = "RenameDialogInputText/PlaceholderText".GetLocalizedResource()
6564
};
6665

@@ -134,5 +133,83 @@ public static DynamicDialog GetFor_FileInUseDialog(List<Shared.Win32Process> loc
134133
});
135134
return dialog;
136135
}
136+
137+
public static DynamicDialog GetFor_CredentialEntryDialog(string path)
138+
{
139+
string[] userAndPass = new string[3];
140+
DynamicDialog? dialog = null;
141+
142+
TextBox inputUsername = new()
143+
{
144+
PlaceholderText = "CredentialDialogUserName/PlaceholderText".GetLocalizedResource()
145+
};
146+
147+
PasswordBox inputPassword = new()
148+
{
149+
PlaceholderText = "CredentialDialogPassword/PlaceholderText".GetLocalizedResource()
150+
};
151+
152+
CheckBox saveCreds = new()
153+
{
154+
Content = "NetworkAuthenticationSaveCheckbox".GetLocalizedResource()
155+
};
156+
157+
inputUsername.TextChanged += (textBox, args) =>
158+
{
159+
userAndPass[0] = inputUsername.Text;
160+
dialog.ViewModel.AdditionalData = userAndPass;
161+
};
162+
163+
inputPassword.PasswordChanged += (textBox, args) =>
164+
{
165+
userAndPass[1] = inputPassword.Password;
166+
dialog.ViewModel.AdditionalData = userAndPass;
167+
};
168+
169+
saveCreds.Checked += (textBox, args) =>
170+
{
171+
userAndPass[2] = "y";
172+
dialog.ViewModel.AdditionalData = userAndPass;
173+
};
174+
175+
saveCreds.Unchecked += (textBox, args) =>
176+
{
177+
userAndPass[2] = "n";
178+
dialog.ViewModel.AdditionalData = userAndPass;
179+
};
180+
181+
dialog = new DynamicDialog(new DynamicDialogViewModel()
182+
{
183+
TitleText = "NetworkAuthenticationDialogTitle".GetLocalizedResource(),
184+
PrimaryButtonText = "AskCredentialDialog/PrimaryButtonText".GetLocalizedResource(),
185+
CloseButtonText = "Cancel".GetLocalizedResource(),
186+
SubtitleText = string.Format("NetworkAuthenticationDialogMessage".GetLocalizedResource(), path.Substring(2)),
187+
DisplayControl = new Grid()
188+
{
189+
MinWidth = 250d,
190+
Children =
191+
{
192+
new StackPanel()
193+
{
194+
Spacing = 10d,
195+
Children =
196+
{
197+
inputUsername,
198+
inputPassword,
199+
saveCreds
200+
}
201+
}
202+
}
203+
},
204+
CloseButtonAction = (vm, e) =>
205+
{
206+
dialog.ViewModel.AdditionalData = null;
207+
vm.HideDialog();
208+
}
209+
210+
});
211+
212+
return dialog;
213+
}
137214
}
138215
}

src/Files.App/Helpers/NavigationHelpers.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ private static async Task<FilesystemResult> OpenDirectory(string path, IShellPag
254254
var opened = (FilesystemResult)false;
255255
bool isHiddenItem = NativeFileOperationsHelper.HasFileAttribute(path, System.IO.FileAttributes.Hidden);
256256
bool isShortcut = FileExtensionHelpers.IsShortcutOrUrlFile(path);
257+
bool isNetwork = path.StartsWith(@"\\", StringComparison.Ordinal);
257258

258259
if (isShortcut)
259260
{
@@ -273,6 +274,27 @@ private static async Task<FilesystemResult> OpenDirectory(string path, IShellPag
273274
await OpenPath(forceOpenInNewTab, userSettingsService.FoldersSettingsService.OpenFoldersInNewTab, path, associatedInstance);
274275
opened = (FilesystemResult)true;
275276
}
277+
else if (isNetwork)
278+
{
279+
var auth = await NetworkDrivesAPI.AuthenticateNetworkShare(path);
280+
if (auth)
281+
{
282+
if (forceOpenInNewTab || userSettingsService.FoldersSettingsService.OpenFoldersInNewTab)
283+
{
284+
await OpenPathInNewTab(path);
285+
}
286+
else
287+
{
288+
associatedInstance.ToolbarViewModel.PathControlDisplayText = path;
289+
associatedInstance.NavigateWithArguments(associatedInstance.InstanceViewModel.FolderSettings.GetLayoutType(path), new NavigationArguments()
290+
{
291+
NavPathParam = path,
292+
AssociatedTabInstance = associatedInstance
293+
});
294+
}
295+
opened = (FilesystemResult)true;
296+
}
297+
}
276298
else
277299
{
278300
opened = await associatedInstance.FilesystemViewModel.GetFolderWithPathFromPathAsync(path)

src/Files.App/Strings/en-US/Resources.resw

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2900,5 +2900,17 @@
29002900
</data>
29012901
<data name="DisplayEditTagsMenu" xml:space="preserve">
29022902
<value>Display the edit tags flyout</value>
2903+
</data>
2904+
<data name="NetworkAuthenticationDialogMessage" xml:space="preserve">
2905+
<value>Enter your credentials to connect to: {0}</value>
2906+
</data>
2907+
<data name="NetworkAuthenticationDialogTitle" xml:space="preserve">
2908+
<value>Enter Network Credentials</value>
2909+
</data>
2910+
<data name="NetworkAuthenticationSaveCheckbox" xml:space="preserve">
2911+
<value>Remember my credentials</value>
2912+
</data>
2913+
<data name="NetworkFolderErrorDialogTitle" xml:space="preserve">
2914+
<value>Network folder error</value>
29032915
</data>
29042916
</root>

src/Files.App/ViewModels/ToolbarViewModel.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,13 @@ public async Task CheckPathInput(string currentInput, string currentSelectedPath
950950
if (currentInput.StartsWith('\\') && !currentInput.StartsWith("\\\\", StringComparison.Ordinal))
951951
currentInput = currentInput.Insert(0, "\\");
952952

953+
if (currentInput.StartsWith('\\'))
954+
{
955+
var auth = await NetworkDrivesAPI.AuthenticateNetworkShare(currentInput);
956+
if (!auth)
957+
return;
958+
}
959+
953960
if (currentSelectedPath == currentInput || string.IsNullOrWhiteSpace(currentInput))
954961
return;
955962

0 commit comments

Comments
 (0)