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+ }
0 commit comments