Files
mus65 933613e31c Update to MSTest 4 (#1721)
* Update to MSTest 4

* fix TestMethodForPlatformAttribute

replace Execute with ExecuteAsync and fix MSTEST0057

https://learn.microsoft.com/en-us/dotnet/core/testing/mstest-analyzers/mstest0057 (link currently dead)

* fix compilation error

* fix MSTEST0037

https://learn.microsoft.com/en-us/dotnet/core/testing/mstest-analyzers/mstest0037

* fix MSTEST0052

https://learn.microsoft.com/en-us/dotnet/core/testing/mstest-analyzers/mstest0052

* fix MSTEST0045

Fixing this properly would require the tests to respect
testContext.CancellationToken. I'm not sure this is worth fixing
or how to even do it for the sync methods.

https://learn.microsoft.com/en-us/dotnet/core/testing/mstest-analyzers/mstest0045

* fix MSTEST0001

I assume that parallelization would break a lot of stuff.

https://learn.microsoft.com/en-us/dotnet/core/testing/mstest-analyzers/mstest0001

* Workaround for new Sonar warnings because of MSTest4

* revert analyzer fixes in OrderedDictionaryTest

* use custom sync console logger for MSTest

to work around https://github.com/microsoft/testfx/issues/6457

* remove redundant args

---------

Co-authored-by: Wojciech Nagórski <wojtpl2@gmail.com>
Co-authored-by: Rob Hague <rob.hague00@gmail.com>
2025-11-14 22:09:13 +01:00

445 lines
19 KiB
C#

using Renci.SshNet.Common;
using Renci.SshNet.IntegrationTests.Common;
namespace Renci.SshNet.IntegrationTests
{
[TestClass]
public class AuthenticationTests : IntegrationTestBase
{
private AuthenticationMethodFactory _authenticationMethodFactory;
private IConnectionInfoFactory _connectionInfoFactory;
private IConnectionInfoFactory _adminConnectionInfoFactory;
private RemoteSshdConfig _remoteSshdConfig;
[TestInitialize]
public void SetUp()
{
_authenticationMethodFactory = new AuthenticationMethodFactory();
_connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort, _authenticationMethodFactory);
_adminConnectionInfoFactory = new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort);
_remoteSshdConfig = new RemoteSshd(_adminConnectionInfoFactory).OpenConfig();
}
[TestCleanup]
public void TearDown()
{
_remoteSshdConfig?.Reset();
using (var client = new SshClient(_adminConnectionInfoFactory.Create()))
{
client.Connect();
// Reset the password back to the "regular" password.
using (var cmd = client.RunCommand($"echo \"{Users.Regular.Password}\n{Users.Regular.Password}\" | sudo passwd " + Users.Regular.UserName))
{
Assert.AreEqual(0, cmd.ExitStatus, cmd.Error);
}
// Remove password expiration
using (var cmd = client.RunCommand($"sudo chage --expiredate -1 " + Users.Regular.UserName))
{
Assert.AreEqual(0, cmd.ExitStatus, cmd.Error);
}
}
}
[TestMethod]
public void Multifactor_PublicKey()
{
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey")
.Update()
.Restart();
var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod());
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
}
}
[TestMethod]
[TestCategory("Authentication")]
public void Multifactor_PublicKey_Connect_Then_Reconnect()
{
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey")
.Update()
.Restart();
var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod());
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
client.Disconnect();
client.Connect();
client.Disconnect();
}
}
[TestMethod]
public void Multifactor_PublicKeyWithPassPhrase()
{
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey")
.Update()
.Restart();
var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyWithPassPhraseAuthenticationMethod());
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
}
}
[TestMethod]
public void Multifactor_PublicKey_MultiplePrivateKey()
{
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey")
.Update()
.Restart();
var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserMultiplePrivateKeyAuthenticationMethod());
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
}
}
[TestMethod]
public void Multifactor_PublicKey_MultipleAuthenticationMethod()
{
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey")
.Update()
.Restart();
var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod(),
_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod());
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
}
}
[TestMethod]
public void Multifactor_KeyboardInteractiveAndPublicKey()
{
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "keyboard-interactive,publickey")
.WithChallengeResponseAuthentication(true)
.WithKeyboardInteractiveAuthentication(true)
.WithUsePAM(true)
.Update()
.Restart();
var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethodWithBadPassword(),
_authenticationMethodFactory.CreateRegularUserKeyboardInteractiveAuthenticationMethod(),
_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod());
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
}
}
[TestMethod]
public void Multifactor_Password_ExceedsPartialSuccessLimit()
{
// configure server to require more successfull authentications from a given method than our partial
// success limit (5) allows
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password,password,password,password,password,password")
.Update()
.Restart();
var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod());
using (var client = new SftpClient(connectionInfo))
{
try
{
client.Connect();
Assert.Fail();
}
catch (SshAuthenticationException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual("Reached authentication attempt limit for method (password).", ex.Message);
}
}
}
[TestMethod]
public void Multifactor_Password_MatchPartialSuccessLimit()
{
// configure server to require a number of successfull authentications from a given method that exactly
// matches our partial success limit (5)
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password,password,password,password,password")
.Update()
.Restart();
var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod());
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
}
}
[TestMethod]
public void Multifactor_Password_Or_PublicKeyAndKeyboardInteractive()
{
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password publickey,keyboard-interactive")
.WithChallengeResponseAuthentication(true)
.WithKeyboardInteractiveAuthentication(true)
.WithUsePAM(true)
.Update()
.Restart();
var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod(),
_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod());
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
}
}
[TestMethod]
public void Multifactor_Password_Or_PublicKeyAndPassword_BadPassword()
{
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password publickey,password")
.Update()
.Restart();
var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethodWithBadPassword(),
_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod());
using (var client = new SftpClient(connectionInfo))
{
try
{
client.Connect();
Assert.Fail();
}
catch (SshAuthenticationException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual("Permission denied (password).", ex.Message);
}
}
}
[TestMethod]
public void Multifactor_PasswordAndPublicKey_Or_PasswordAndPassword()
{
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password,publickey password,password")
.Update()
.Restart();
var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod(),
_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethodWithBadKey());
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
}
connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethodWithBadPassword(),
_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod());
using (var client = new SftpClient(connectionInfo))
{
try
{
client.Connect();
Assert.Fail();
}
catch (SshAuthenticationException ex)
{
Assert.IsNull(ex.InnerException);
Assert.AreEqual("Permission denied (password).", ex.Message);
}
}
}
[TestMethod]
public void Multifactor_PasswordAndPassword_Or_PublicKey()
{
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password,password publickey")
.Update()
.Restart();
var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod(),
_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethodWithBadKey());
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
}
connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod());
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
}
}
[TestMethod]
public void Multifactor_Password_Or_Password()
{
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password password")
.Update()
.Restart();
var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod());
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
}
connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod(),
_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethodWithBadKey());
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
}
}
[TestMethod]
public void KeyboardInteractive_PasswordExpired()
{
var temporaryPassword = new Random().Next().ToString();
using (var client = new SshClient(_adminConnectionInfoFactory.Create()))
{
client.Connect();
// Temporarity modify password so that when we expire this password, we change reset the password back to
// the "regular" password.
using (var cmd = client.RunCommand($"echo \"{temporaryPassword}\n{temporaryPassword}\" | sudo passwd " + Users.Regular.UserName))
{
Assert.AreEqual(0, cmd.ExitStatus, cmd.Error);
}
// Force the password to expire immediately
using (var cmd = client.RunCommand($"sudo chage -d 0 " + Users.Regular.UserName))
{
Assert.AreEqual(0, cmd.ExitStatus, cmd.Error);
}
}
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "keyboard-interactive")
.WithChallengeResponseAuthentication(true)
.WithKeyboardInteractiveAuthentication(true)
.WithUsePAM(true)
.Update()
.Restart();
var keyboardInteractive = new KeyboardInteractiveAuthenticationMethod(Users.Regular.UserName);
int authenticationPromptCount = 0;
keyboardInteractive.AuthenticationPrompt += (sender, args) =>
{
Console.WriteLine(args.Instruction);
foreach (var authenticationPrompt in args.Prompts)
{
Console.WriteLine(authenticationPrompt.Request);
switch (authenticationPromptCount)
{
case 0:
// Regular password prompt
authenticationPrompt.Response = temporaryPassword;
break;
case 1:
// Password expired, provide current password
authenticationPrompt.Response = temporaryPassword;
break;
case 2:
// Password expired, provide new password
authenticationPrompt.Response = Users.Regular.Password;
break;
case 3:
// Password expired, retype new password
authenticationPrompt.Response = Users.Regular.Password;
break;
default:
break;
}
authenticationPromptCount++;
}
};
var connectionInfo = _connectionInfoFactory.Create(keyboardInteractive);
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
Assert.AreEqual(4, authenticationPromptCount);
}
}
[TestMethod]
public void KeyboardInteractiveConnectionInfo()
{
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "keyboard-interactive")
.WithChallengeResponseAuthentication(true)
.WithKeyboardInteractiveAuthentication(true)
.WithUsePAM(true)
.Update()
.Restart();
var host = SshServerHostName;
var port = SshServerPort;
var username = User.UserName;
var password = User.Password;
#region Example KeyboardInteractiveConnectionInfo AuthenticationPrompt
var connectionInfo = new KeyboardInteractiveConnectionInfo(host, port, username);
connectionInfo.AuthenticationPrompt += delegate (object sender, AuthenticationPromptEventArgs e)
{
Console.WriteLine(e.Instruction);
foreach (var prompt in e.Prompts)
{
Console.WriteLine(prompt.Request);
prompt.Response = password;
}
};
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
// Do something here
client.Disconnect();
}
#endregion
Assert.AreEqual(connectionInfo.Host, SshServerHostName);
Assert.AreEqual(connectionInfo.Username, User.UserName);
}
[TestMethod]
public void KeyboardInteractive_NoResponseSet_ThrowsSshAuthenticationException()
{
// ...instead of a cryptic ArgumentNullException
// https://github.com/sshnet/SSH.NET/issues/382
_remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "keyboard-interactive")
.WithChallengeResponseAuthentication(true)
.WithKeyboardInteractiveAuthentication(true)
.WithUsePAM(true)
.Update()
.Restart();
var connectionInfo = _connectionInfoFactory.Create(new KeyboardInteractiveAuthenticationMethod(Users.Regular.UserName));
using (var client = new SftpClient(connectionInfo))
{
try
{
client.Connect();
Assert.Fail();
}
catch (SshAuthenticationException ex)
{
Assert.IsNull(ex.InnerException);
Assert.StartsWith("AuthenticationPrompt.Response is null for prompt \"Password: \"", ex.Message);
}
}
}
}
}