2424using Microsoft . Extensions . Localization ;
2525using Microsoft . Extensions . Options ;
2626using OutOfSchool . IdentityServer . Config ;
27+ using OutOfSchool . IdentityServer . Services . Interfaces ;
28+ using OutOfSchool . Tests . Common . TestDataGenerators ;
2729
2830namespace OutOfSchool . IdentityServer . Tests . Controllers ;
2931
3032[ TestFixture ]
3133public class AuthControllerTests
3234{
3335 private readonly Mock < FakeUserManager > fakeUserManager ;
36+ private readonly Mock < IUserManagerAdditionalService > fakeUserManagerAdditionalService ;
3437 private readonly Mock < FakeSignInManager > fakeSignInManager ;
3538 private readonly Mock < IIdentityServerInteractionService > fakeInteractionService ;
3639 private readonly Mock < ILogger < AuthController > > fakeLogger ;
@@ -42,6 +45,7 @@ public class AuthControllerTests
4245 public AuthControllerTests ( )
4346 {
4447 fakeUserManager = new Mock < FakeUserManager > ( ) ;
48+ fakeUserManagerAdditionalService = new Mock < IUserManagerAdditionalService > ( ) ;
4549 fakeInteractionService = new Mock < IIdentityServerInteractionService > ( ) ;
4650 fakeSignInManager = new Mock < FakeSignInManager > ( ) ;
4751 fakeLogger = new Mock < ILogger < AuthController > > ( ) ;
@@ -67,6 +71,7 @@ public void Setup()
6771 . Returns ( new LocalizedString ( "mock" , "error" ) ) ;
6872 authController = new AuthController (
6973 fakeUserManager . Object ,
74+ fakeUserManagerAdditionalService . Object ,
7075 fakeSignInManager . Object ,
7176 fakeInteractionService . Object ,
7277 fakeLogger . Object ,
@@ -168,6 +173,183 @@ public async Task Login_WithLoginVMWithoutModelError_ReturnsActionResult(
168173 Assert . IsInstanceOf ( type , result ) ;
169174 }
170175
176+ [ Test ]
177+ public async Task Login_WhenUserMustChangePasswordAndEmailWithPasswordAreCorrect_ReturnsRedirectActionToChangePasswordLogin ( )
178+ {
179+ // Arrange
180+ var user = UserGenerator . Generate ( ) ;
181+ var loginViewModel = CreateLoginViewModelFromData ( ) ;
182+ user . MustChangePassword = true ;
183+ SetupDefaultUserManagerFindByEmailAsync ( user ) ;
184+ SetupDefaultSignInManagerCheckPasswordSignInAsync ( SignInResult . Success ) ;
185+
186+ // Act
187+ var result = await authController . Login ( loginViewModel ) ;
188+
189+ // Assert
190+ Assert . IsInstanceOf ( typeof ( RedirectToActionResult ) , result ) ;
191+ Assert . AreEqual ( 0 , authController . ModelState . Count ) ;
192+ Assert . AreEqual ( nameof ( AuthController . ChangePasswordLogin ) , ( result as RedirectToActionResult ) ? . ActionName ) ;
193+ }
194+
195+ [ Test ]
196+ public async Task Login_WhenUserMustChangePasswordAndEmailWithPasswordAreNotCorrect_ReturnsViewWithError ( )
197+ {
198+ // Arrange
199+ var user = UserGenerator . Generate ( ) ;
200+ var loginViewModel = CreateLoginViewModelFromData ( ) ;
201+ user . MustChangePassword = true ;
202+ SetupDefaultUserManagerFindByEmailAsync ( user ) ;
203+ SetupDefaultSignInManagerCheckPasswordSignInAsync ( SignInResult . Failed ) ;
204+
205+ // Act
206+ var result = await authController . Login ( loginViewModel ) ;
207+
208+ // Assert
209+ Assert . IsInstanceOf ( typeof ( ViewResult ) , result ) ;
210+ Assert . AreEqual ( 1 , authController . ModelState . Count ) ;
211+ }
212+
213+ [ Test ]
214+ public async Task Login_WhenUserMustNotChangePasswordAndEmailWithPasswordAreCorrectAndReturnUrlIsNotEmpty_ReturnsRedirectToReturnUrl ( )
215+ {
216+ // Arrange
217+ var user = UserGenerator . Generate ( ) ;
218+ var loginViewModel = CreateLoginViewModelFromData ( user . UserName ) ;
219+ user . MustChangePassword = false ;
220+ SetupDefaultUserManagerFindByEmailAsync ( user ) ;
221+ SetupDefaultUserManagerUpdateAsync ( IdentityResult . Success ) ;
222+ SetupDefaultSignInManagerPasswordSignInAsync ( SignInResult . Success ) ;
223+
224+ // Act
225+ var result = await authController . Login ( loginViewModel ) ;
226+
227+ // Assert
228+ Assert . IsInstanceOf ( typeof ( RedirectResult ) , result ) ;
229+ Assert . AreEqual ( 0 , authController . ModelState . Count ) ;
230+ Assert . AreEqual ( loginViewModel . ReturnUrl , ( result as RedirectResult ) ? . Url ) ;
231+ }
232+
233+ [ Test ]
234+ public async Task Login_WhenUserMustNotChangePasswordAndEmailWithPasswordAreCorrectAndReturnUrlIsEmpty_ReturnsRedirectToLogin ( )
235+ {
236+ // Arrange
237+ var user = UserGenerator . Generate ( ) ;
238+ var loginViewModel = CreateLoginViewModelFromData ( user . UserName , returnUrl : string . Empty ) ;
239+ user . MustChangePassword = false ;
240+ SetupDefaultUserManagerFindByEmailAsync ( user ) ;
241+ SetupDefaultUserManagerUpdateAsync ( IdentityResult . Success ) ;
242+ SetupDefaultSignInManagerPasswordSignInAsync ( SignInResult . Success ) ;
243+
244+ // Act
245+ var result = await authController . Login ( loginViewModel ) ;
246+
247+ // Assert
248+ Assert . IsInstanceOf ( typeof ( RedirectResult ) , result ) ;
249+ Assert . AreEqual ( 0 , authController . ModelState . Count ) ;
250+ Assert . AreEqual ( nameof ( AuthController . Login ) , ( result as RedirectResult ) ? . Url ) ;
251+ }
252+
253+ [ Test ]
254+ public async Task ChangePasswordLogin_WhenUserMustChangePasswordAndEmailWithPasswordsAreCorrectAndReturnUrlIsNotEmpty_ReturnsRedirectToReturnUrl ( )
255+ {
256+ // Arrange
257+ var user = UserGenerator . Generate ( ) ;
258+ user . MustChangePassword = true ;
259+ var changePasswordLoginViewModel = CreateChangePasswordLoginViewModelFromData ( ) ;
260+ SetupDefaultUserManagerFindByEmailAsync ( user ) ;
261+ fakeUserManagerAdditionalService . Setup ( s =>
262+ s . ChangePasswordWithRequiredMustChangePasswordAsync ( user , It . IsAny < string > ( ) , It . IsAny < string > ( ) ) )
263+ . ReturnsAsync ( ( ) => { user . MustChangePassword = false ; return IdentityResult . Success ; } ) ;
264+ SetupDefaultSignInManagerCheckPasswordSignInAsync ( SignInResult . Success ) ;
265+ fakeSignInManager . Setup ( s => s . GetExternalAuthenticationSchemesAsync ( ) )
266+ . ReturnsAsync ( new List < AuthenticationScheme > ( ) ) ;
267+ SetupDefaultUserManagerUpdateAsync ( IdentityResult . Success ) ;
268+ SetupDefaultSignInManagerPasswordSignInAsync ( SignInResult . Success ) ;
269+
270+ // Act
271+ var result = await authController . ChangePasswordLogin ( changePasswordLoginViewModel ) ;
272+
273+ // Assert
274+ Assert . IsInstanceOf ( typeof ( RedirectResult ) , result ) ;
275+ Assert . AreEqual ( 0 , authController . ModelState . Count ) ;
276+ Assert . AreEqual ( changePasswordLoginViewModel . ReturnUrl , ( result as RedirectResult ) ? . Url ) ;
277+ }
278+
279+ [ Test ]
280+ public async Task ChangePasswordLogin_WhenUserMustChangePasswordAndEmailWithPasswordsAreCorrectAndReturnUrlIsEmpty_ReturnsRedirectToLogin ( )
281+ {
282+ // Arrange
283+ var user = UserGenerator . Generate ( ) ;
284+ user . MustChangePassword = true ;
285+ var changePasswordLoginViewModel = CreateChangePasswordLoginViewModelFromData ( returnUrl : string . Empty ) ;
286+ SetupDefaultUserManagerFindByEmailAsync ( user ) ;
287+ fakeUserManagerAdditionalService . Setup ( s =>
288+ s . ChangePasswordWithRequiredMustChangePasswordAsync ( user , It . IsAny < string > ( ) , It . IsAny < string > ( ) ) )
289+ . ReturnsAsync ( ( ) => { user . MustChangePassword = false ; return IdentityResult . Success ; } ) ;
290+ SetupDefaultSignInManagerCheckPasswordSignInAsync ( SignInResult . Success ) ;
291+ fakeSignInManager . Setup ( s => s . GetExternalAuthenticationSchemesAsync ( ) )
292+ . ReturnsAsync ( new List < AuthenticationScheme > ( ) ) ;
293+ SetupDefaultUserManagerUpdateAsync ( IdentityResult . Success ) ;
294+ SetupDefaultSignInManagerPasswordSignInAsync ( SignInResult . Success ) ;
295+
296+ // Act
297+ var result = await authController . ChangePasswordLogin ( changePasswordLoginViewModel ) ;
298+
299+ // Assert
300+ Assert . IsInstanceOf ( typeof ( RedirectResult ) , result ) ;
301+ Assert . AreEqual ( 0 , authController . ModelState . Count ) ;
302+ Assert . AreEqual ( nameof ( AuthController . Login ) , ( result as RedirectResult ) ? . Url ) ;
303+ }
304+
305+ [ Test ]
306+ public async Task ChangePasswordLogin_WhenUserMustNotChangePassword_ReturnsViewWithModelError ( )
307+ {
308+ // Arrange
309+ var user = UserGenerator . Generate ( ) ;
310+ user . MustChangePassword = false ;
311+ var changePasswordLoginViewModel = CreateChangePasswordLoginViewModelFromData ( returnUrl : string . Empty ) ;
312+ SetupDefaultUserManagerFindByEmailAsync ( user ) ;
313+
314+ // Act
315+ var result = await authController . ChangePasswordLogin ( changePasswordLoginViewModel ) ;
316+
317+ // Assert
318+ Assert . IsInstanceOf ( typeof ( ViewResult ) , result ) ;
319+ Assert . AreEqual ( 1 , authController . ModelState . Count ) ;
320+ }
321+
322+ [ Test ] public async Task ChangePasswordLogin_WhenUserMustChangePasswordAndEmailWithPasswordAreNotCorrect_ReturnsViewWithModelError ( )
323+ {
324+ // Arrange
325+ var user = UserGenerator . Generate ( ) ;
326+ user . MustChangePassword = true ;
327+ var changePasswordLoginViewModel = CreateChangePasswordLoginViewModelFromData ( ) ;
328+ SetupDefaultUserManagerFindByEmailAsync ( user ) ;
329+ SetupDefaultSignInManagerCheckPasswordSignInAsync ( SignInResult . Failed ) ;
330+
331+ // Act
332+ var result = await authController . ChangePasswordLogin ( changePasswordLoginViewModel ) ;
333+
334+ // Assert
335+ Assert . IsInstanceOf ( typeof ( ViewResult ) , result ) ;
336+ Assert . AreEqual ( 1 , authController . ModelState . Count ) ;
337+ }
338+
339+ [ Test ] public async Task ChangePasswordLogin_WhenUserNotFound_ReturnsViewWithModelError ( )
340+ {
341+ // Arrange
342+ var changePasswordLoginViewModel = CreateChangePasswordLoginViewModelFromData ( ) ;
343+ SetupDefaultUserManagerFindByEmailAsync ( null ) ;
344+
345+ // Act
346+ var result = await authController . ChangePasswordLogin ( changePasswordLoginViewModel ) ;
347+
348+ // Assert
349+ Assert . IsInstanceOf ( typeof ( ViewResult ) , result ) ;
350+ Assert . AreEqual ( 1 , authController . ModelState . Count ) ;
351+ }
352+
171353 [ Test ]
172354 public void Register_WithoutReturnUrl_ReturnsViewResult ( )
173355 {
@@ -339,4 +521,51 @@ public static IEnumerable<AuthenticationScheme> EmptySchemes
339521 {
340522 get => new List < AuthenticationScheme > ( ) ;
341523 }
524+
525+ private void SetupDefaultUserManagerFindByEmailAsync ( User user )
526+ => fakeUserManager . Setup ( s => s . FindByEmailAsync ( It . IsAny < string > ( ) ) ) . ReturnsAsync ( user ) ;
527+
528+ private void SetupDefaultSignInManagerCheckPasswordSignInAsync ( SignInResult signInResult )
529+ => fakeSignInManager . Setup ( s => s
530+ . CheckPasswordSignInAsync ( It . IsAny < User > ( ) , It . IsAny < string > ( ) , false ) )
531+ . ReturnsAsync ( signInResult ) ;
532+
533+ private void SetupDefaultUserManagerUpdateAsync ( IdentityResult identityResult )
534+ => fakeUserManager . Setup ( s => s
535+ . UpdateAsync ( It . IsAny < User > ( ) ) ) . ReturnsAsync ( identityResult ) ;
536+
537+ private void SetupDefaultSignInManagerPasswordSignInAsync ( SignInResult signInResult )
538+ => fakeSignInManager . Setup ( s => s . PasswordSignInAsync ( It . IsAny < string > ( ) , It . IsAny < string > ( ) , false , false ) )
539+ . ReturnsAsync ( signInResult ) ;
540+ private static LoginViewModel CreateLoginViewModelFromData (
541+ string email = "[email protected] " , 542+ string password = "RandomPassword3%" ,
543+ string returnUrl = "RandomReturnUrl" ,
544+ IEnumerable < AuthenticationScheme > authenticationSchemes = default )
545+ {
546+ return new LoginViewModel
547+ {
548+ Username = email ,
549+ Password = password ,
550+ ReturnUrl = returnUrl ,
551+ ExternalProviders = authenticationSchemes
552+ } ;
553+ }
554+
555+ private static ChangePasswordLoginViewModel CreateChangePasswordLoginViewModelFromData (
556+ string email = "[email protected] " , 557+ string password = "RandomPassword5%" ,
558+ string newPassword = "RandomPassword3%" ,
559+ string confirmNewPassword = "RandomPassword3%" ,
560+ string returnUrl = "RandomReturnUrl" )
561+ {
562+ return new ChangePasswordLoginViewModel
563+ {
564+ Email = email ,
565+ CurrentPassword = password ,
566+ NewPassword = newPassword ,
567+ ConfirmNewPassword = confirmNewPassword ,
568+ ReturnUrl = returnUrl
569+ } ;
570+ }
342571}
0 commit comments