Skip to content

Commit c2e1de5

Browse files
committed
Improve tests for partial success limit.
1 parent 14b654d commit c2e1de5

12 files changed

+609
-112
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
using System.Collections.Generic;
2+
using Microsoft.VisualStudio.TestTools.UnitTesting;
3+
using Moq;
4+
using Renci.SshNet.Common;
5+
6+
namespace Renci.SshNet.Tests.Classes
7+
{
8+
/// <summary>
9+
/// * ConnectionInfo provides the following authentication methods (in order):
10+
/// o publickey
11+
/// o password
12+
/// * Partial success limit is 2
13+
///
14+
/// none
15+
/// (1=FAIL)
16+
/// |
17+
/// +------------------------+------------------------+
18+
/// | | |
19+
/// password ◄--\ publickey keyboard-interactive
20+
/// (7=SKIP) | (2=PS)
21+
/// | |
22+
/// | password
23+
/// | (3=PS)
24+
/// | |
25+
/// | password
26+
/// | (4=PS)
27+
/// | |
28+
/// | publickey
29+
/// | (5=PS)
30+
/// | |
31+
/// \---- publickey
32+
/// (6=SKIP)
33+
/// </summary>
34+
[TestClass]
35+
public class ClientAuthenticationTest_Failure_MultiList_AllAllowedAuthenticationsHaveReachedPartialSuccessLimit : ClientAuthenticationTestBase
36+
{
37+
private int _partialSuccessLimit;
38+
private ClientAuthentication _clientAuthentication;
39+
private SshAuthenticationException _actualException;
40+
41+
protected override void SetupData()
42+
{
43+
_partialSuccessLimit = 2;
44+
}
45+
46+
protected override void SetupMocks()
47+
{
48+
var seq = new MockSequence();
49+
50+
SessionMock.InSequence(seq).Setup(p => p.RegisterMessage("SSH_MSG_USERAUTH_FAILURE"));
51+
SessionMock.InSequence(seq).Setup(p => p.RegisterMessage("SSH_MSG_USERAUTH_SUCCESS"));
52+
SessionMock.InSequence(seq).Setup(p => p.RegisterMessage("SSH_MSG_USERAUTH_BANNER"));
53+
54+
ConnectionInfoMock.InSequence(seq).Setup(p => p.CreateNoneAuthenticationMethod())
55+
.Returns(NoneAuthenticationMethodMock.Object);
56+
57+
/* 1 */
58+
59+
NoneAuthenticationMethodMock.InSequence(seq).Setup(p => p.Authenticate(SessionMock.Object))
60+
.Returns(AuthenticationResult.Failure);
61+
ConnectionInfoMock.InSequence(seq)
62+
.Setup(p => p.AuthenticationMethods)
63+
.Returns(new List<IAuthenticationMethod>
64+
{
65+
PublicKeyAuthenticationMethodMock.Object,
66+
PasswordAuthenticationMethodMock.Object
67+
});
68+
NoneAuthenticationMethodMock.InSequence(seq)
69+
.Setup(p => p.AllowedAuthentications)
70+
.Returns(new[] {"password", "publickey", "keyboard-interactive"});
71+
72+
/* Enumerate supported authentication methods */
73+
74+
PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("publickey");
75+
PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("password");
76+
77+
/* 2 */
78+
79+
PublicKeyAuthenticationMethodMock.InSequence(seq)
80+
.Setup(p => p.Authenticate(SessionMock.Object))
81+
.Returns(AuthenticationResult.PartialSuccess);
82+
PublicKeyAuthenticationMethodMock.InSequence(seq)
83+
.Setup(p => p.AllowedAuthentications)
84+
.Returns(new[] {"password"});
85+
86+
/* Enumerate supported authentication methods */
87+
88+
PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("publickey");
89+
PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("password");
90+
91+
/* 3 */
92+
93+
PasswordAuthenticationMethodMock.InSequence(seq)
94+
.Setup(p => p.Authenticate(SessionMock.Object))
95+
.Returns(AuthenticationResult.PartialSuccess);
96+
PasswordAuthenticationMethodMock.InSequence(seq)
97+
.Setup(p => p.AllowedAuthentications)
98+
.Returns(new[] {"password"});
99+
100+
/* Enumerate supported authentication methods */
101+
102+
PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("publickey");
103+
PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("password");
104+
105+
/* 4 */
106+
107+
PasswordAuthenticationMethodMock.InSequence(seq)
108+
.Setup(p => p.Authenticate(SessionMock.Object))
109+
.Returns(AuthenticationResult.PartialSuccess);
110+
PasswordAuthenticationMethodMock.InSequence(seq)
111+
.Setup(p => p.AllowedAuthentications)
112+
.Returns(new[] {"publickey"});
113+
114+
/* Enumerate supported authentication methods */
115+
116+
PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("publickey");
117+
PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("password");
118+
119+
/* 5 */
120+
121+
PublicKeyAuthenticationMethodMock.InSequence(seq)
122+
.Setup(p => p.Authenticate(SessionMock.Object))
123+
.Returns(AuthenticationResult.PartialSuccess);
124+
PublicKeyAuthenticationMethodMock.InSequence(seq)
125+
.Setup(p => p.AllowedAuthentications)
126+
.Returns(new[] {"publickey"});
127+
128+
/* Enumerate supported authentication methods */
129+
130+
PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("publickey");
131+
PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("password");
132+
133+
/* 6: Record partial success limit reached exception, and skip password authentication method */
134+
135+
PublicKeyAuthenticationMethodMock.InSequence(seq)
136+
.Setup(p => p.Name)
137+
.Returns("publickey-partial1");
138+
139+
/* 7: Record partial success limit reached exception, and skip password authentication method */
140+
141+
PasswordAuthenticationMethodMock.InSequence(seq)
142+
.Setup(p => p.Name)
143+
.Returns("password-partial1");
144+
145+
SessionMock.InSequence(seq).Setup(p => p.UnRegisterMessage("SSH_MSG_USERAUTH_FAILURE"));
146+
SessionMock.InSequence(seq).Setup(p => p.UnRegisterMessage("SSH_MSG_USERAUTH_SUCCESS"));
147+
SessionMock.InSequence(seq).Setup(p => p.UnRegisterMessage("SSH_MSG_USERAUTH_BANNER"));
148+
}
149+
150+
protected override void Arrange()
151+
{
152+
base.Arrange();
153+
154+
_clientAuthentication = new ClientAuthentication(_partialSuccessLimit);
155+
}
156+
157+
protected override void Act()
158+
{
159+
try
160+
{
161+
_clientAuthentication.Authenticate(ConnectionInfoMock.Object, SessionMock.Object);
162+
Assert.Fail();
163+
}
164+
catch (SshAuthenticationException ex)
165+
{
166+
_actualException = ex;
167+
}
168+
}
169+
170+
[TestMethod]
171+
public void AuthenticateOnPasswordAuthenticationMethodShouldHaveBeenInvokedTwice()
172+
{
173+
PasswordAuthenticationMethodMock.Verify(p => p.Authenticate(SessionMock.Object), Times.Exactly(2));
174+
}
175+
176+
[TestMethod]
177+
public void AuthenticateOnPublicKeyAuthenticationMethodShouldHaveBeenInvokedTwice()
178+
{
179+
PublicKeyAuthenticationMethodMock.Verify(p => p.Authenticate(SessionMock.Object), Times.Exactly(2));
180+
}
181+
182+
[TestMethod]
183+
public void AuthenticateShouldThrowSshAuthenticationException()
184+
{
185+
Assert.IsNotNull(_actualException);
186+
Assert.IsNull(_actualException.InnerException);
187+
Assert.AreEqual("Reached authentication attempt limit for method (password-partial1).", _actualException.Message);
188+
}
189+
}
190+
}

src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Failure_SingleList_AuthenticationMethodNotConfigured.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,17 @@ protected override void SetupMocks()
2828
ConnectionInfoMock.InSequence(seq).Setup(p => p.CreateNoneAuthenticationMethod())
2929
.Returns(NoneAuthenticationMethodMock.Object);
3030

31-
NoneAuthenticationMethodMock.InSequence(seq).Setup(p => p.Authenticate(SessionMock.Object))
32-
.Returns(AuthenticationResult.Failure);
31+
NoneAuthenticationMethodMock.InSequence(seq)
32+
.Setup(p => p.Authenticate(SessionMock.Object))
33+
.Returns(AuthenticationResult.Failure);
3334
ConnectionInfoMock.InSequence(seq).Setup(p => p.AuthenticationMethods)
34-
.Returns(new List<IAuthenticationMethod>
35-
{
36-
PublicKeyAuthenticationMethodMock.Object
37-
});
35+
.Returns(new List<IAuthenticationMethod>
36+
{
37+
PublicKeyAuthenticationMethodMock.Object
38+
});
3839
NoneAuthenticationMethodMock.InSequence(seq)
39-
.Setup(p => p.AllowedAuthentications)
40-
.Returns(new[] { "password" });
40+
.Setup(p => p.AllowedAuthentications)
41+
.Returns(new[] {"password"});
4142

4243
PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("publickey");
4344

src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_AllAllowedAuthenticationsHaveReachedPartialSuccessLimit.cs

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_DifferentAllowedAuthenticationsAfterPartialSuccess.cs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,25 @@
44

55
namespace Renci.SshNet.Tests.Classes
66
{
7+
/// <summary>
8+
/// * ConnectionInfo provides the following authentication methods (in order):
9+
/// o password
10+
/// o publickey
11+
/// o keyboard-interactive
12+
/// * Partial success limit is 1
13+
/// * Scenario:
14+
/// none
15+
/// (1=FAIL)
16+
/// |
17+
/// +------------------------------+
18+
/// | |
19+
/// publickey password
20+
/// (2=PARTIAL)
21+
/// *----------------------*
22+
/// | |
23+
/// keyboard-interactive publickey
24+
/// (3=SUCCESS)
25+
/// </summary>
726
[TestClass]
827
public class ClientAuthenticationTest_Success_MultiList_DifferentAllowedAuthenticationsAfterPartialSuccess : ClientAuthenticationTestBase
928
{
@@ -26,6 +45,8 @@ protected override void SetupMocks()
2645
ConnectionInfoMock.InSequence(seq).Setup(p => p.CreateNoneAuthenticationMethod())
2746
.Returns(NoneAuthenticationMethodMock.Object);
2847

48+
/* 1 */
49+
2950
NoneAuthenticationMethodMock.InSequence(seq).Setup(p => p.Authenticate(SessionMock.Object))
3051
.Returns(AuthenticationResult.Failure);
3152
ConnectionInfoMock.InSequence(seq).Setup(p => p.AuthenticationMethods)
@@ -35,20 +56,34 @@ protected override void SetupMocks()
3556
PublicKeyAuthenticationMethodMock.Object,
3657
KeyboardInteractiveAuthenticationMethodMock.Object,
3758
});
38-
NoneAuthenticationMethodMock.InSequence(seq).Setup(p => p.AllowedAuthentications).Returns(new[] { "publickey", "password" });
59+
NoneAuthenticationMethodMock.InSequence(seq)
60+
.Setup(p => p.AllowedAuthentications)
61+
.Returns(new[] {"publickey", "password"});
62+
63+
/* Enumerate supported authentication methods */
64+
3965
PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("password");
4066
PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("publickey");
4167
KeyboardInteractiveAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("keyboard-interactive");
4268

69+
/* 2 */
70+
4371
PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Authenticate(SessionMock.Object))
4472
.Returns(AuthenticationResult.PartialSuccess);
4573
PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.AllowedAuthentications)
46-
.Returns(new[] { "keyboard-interactive", "publickey" });
74+
.Returns(new[] {"keyboard-interactive", "publickey"});
75+
76+
/* Enumerate supported authentication methods */
77+
4778
PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("password");
4879
PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("publickey");
4980
KeyboardInteractiveAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("keyboard-interactive");
5081

51-
PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Authenticate(SessionMock.Object)).Returns(AuthenticationResult.Success);
82+
/* 3 */
83+
84+
PublicKeyAuthenticationMethodMock.InSequence(seq)
85+
.Setup(p => p.Authenticate(SessionMock.Object))
86+
.Returns(AuthenticationResult.Success);
5287

5388
SessionMock.InSequence(seq).Setup(p => p.UnRegisterMessage("SSH_MSG_USERAUTH_FAILURE"));
5489
SessionMock.InSequence(seq).Setup(p => p.UnRegisterMessage("SSH_MSG_USERAUTH_SUCCESS"));

src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedByFailureInAlternateBranch.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,24 @@ namespace Renci.SshNet.Tests.Classes
1111
/// o publickey
1212
/// o keyboard-interactive
1313
/// * Partial success limit is 2
14-
///
14+
/// * Scenario:
1515
/// none
1616
/// (1=FAIL)
1717
/// |
18-
/// +-------------------+
19-
/// | |
20-
/// publickey keyboard-interactive
21-
/// (2=PS) ^ (6=FAIL)
22-
/// | |
23-
/// password |
24-
/// (3=PS) |
25-
/// | |
26-
/// password |
27-
/// (4=PS) |
28-
/// | |
29-
/// password |
30-
/// (5=SKIP) |
31-
/// +------------+
18+
/// +------------------------+
19+
/// | |
20+
/// publickey keyboard-interactive
21+
/// (2=PS) ^ (6=FAIL)
22+
/// | |
23+
/// password |
24+
/// (3=PS) |
25+
/// | |
26+
/// password |
27+
/// (4=PS) |
28+
/// | |
29+
/// password |
30+
/// (5=SKIP) |
31+
/// +---------------+
3232
/// </summary>
3333
[TestClass]
3434
public class ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedByFailureInAlternateBranch : ClientAuthenticationTestBase

src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedByFailureInAlternateBranch2.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ protected override void SetupMocks()
145145

146146
PasswordAuthenticationMethodMock.InSequence(seq)
147147
.Setup(p => p.Name)
148-
.Returns("password-partial1");
148+
.Returns("password-partial2");
149149

150150
/* 8 */
151151

0 commit comments

Comments
 (0)