package com.mailserver.controller; import com.mailserver.config.MailDomains; import com.mailserver.domain.User; import com.mailserver.dto.LoginRequest; import com.mailserver.security.LoginGuard; import com.mailserver.service.MailService; import com.mailserver.service.UserService; import com.mailserver.util.JwtUtils; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import reactor.core.publisher.Mono; import java.net.InetSocketAddress; import java.time.Duration; import java.util.Map; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class AuthenticationControllerTest { private final UserService users = mock(UserService.class); private final JwtUtils jwt = mock(JwtUtils.class); private final LoginGuard guard = new LoginGuard(new LoginGuard.Settings(true, 3, 100, Duration.ofMinutes(15), Duration.ofMinutes(15), Duration.ofHours(24), Duration.ofDays(30), Set.of(), Duration.ZERO)); private final com.mailserver.config.MailAdmins admins = new com.mailserver.config.MailAdmins("admin@x.test", "", new MailDomains("x.test", "")); private final AuthenticationController controller = new AuthenticationController(users, mock(MailService.class), jwt, mock(MailDomains.class), false, guard, admins, new com.mailserver.config.JambotronSso("", "", "", "mail.x.test")); private final User alice = new User(); AuthenticationControllerTest() { alice.setEmail("alice@x.test"); alice.setPassword("hash"); when(users.findByEmail("alice@x.test")).thenReturn(Mono.just(alice)); when(users.findByEmail("ghost@x.test")).thenReturn(Mono.empty()); when(users.checkPassword(eq(alice), anyString())).thenAnswer(i -> "secret".equals(i.getArgument(1))); when(jwt.generateToken(any())).thenReturn("token"); } private ResponseEntity login(String ip, String email, String password) { LoginRequest request = new LoginRequest(); request.setEmail(email); request.setPassword(password); MockServerHttpRequest http = MockServerHttpRequest.post("/api/login") .remoteAddress(new InetSocketAddress(ip, 40000)).build(); return controller.login(Mono.just(request), http).block(); } // «Sign in with Jambotron» on: the web password only for the built-in administrator @Test void withJambotronSignInOnlyTheBuiltInAdministratorLogsInWithAPassword() { AuthenticationController sso = new AuthenticationController(users, mock(MailService.class), jwt, mock(MailDomains.class), false, guard, admins, new com.mailserver.config.JambotronSso("https://jambotron.test", "tok", "", "mail.x.test")); User admin = new User(); admin.setEmail("admin@x.test"); when(users.findByEmail("admin@x.test")).thenReturn(Mono.just(admin)); when(users.checkPassword(eq(admin), anyString())).thenReturn(true); LoginRequest request = new LoginRequest(); request.setEmail("alice@x.test"); request.setPassword("secret"); MockServerHttpRequest http = MockServerHttpRequest.post("/api/login").remoteAddress(new InetSocketAddress("9.9.9.9", 1)).build(); assertThat(sso.login(Mono.just(request), http).block().getBody()) .isEqualTo(Map.of("error", "Sign in with Jambotron — the password is only for mail programs")); request.setEmail("admin@x.test"); assertThat(sso.login(Mono.just(request), http).block().getStatusCode()).isEqualTo(HttpStatus.OK); } @Test void unknownMailboxAndWrongPasswordLookTheSameAndBothCount() { assertThat(login("1.2.3.4", "ghost@x.test", "x").getBody()).isEqualTo(Map.of("error", "Invalid credentials")); assertThat(login("1.2.3.4", "alice@x.test", "x").getBody()).isEqualTo(Map.of("error", "Invalid credentials")); login("1.2.3.4", "alice@x.test", "y"); ResponseEntity blocked = login("1.2.3.4", "alice@x.test", "secret"); assertThat(blocked.getStatusCode()).isEqualTo(HttpStatus.TOO_MANY_REQUESTS); assertThat(blocked.getHeaders().getFirst("Retry-After")).isEqualTo("900"); assertThat(blocked.getBody()).isEqualTo(Map.of("error", "Too many failed logins, try again in 15 min")); assertThat(login("5.6.7.8", "alice@x.test", "secret").getStatusCode()).isEqualTo(HttpStatus.OK); } @Test void blockedAttemptDoesNotCheckThePassword() { for (int i = 0; i < 3; i++) { login("1.2.3.4", "ghost@x.test", "x"); } login("1.2.3.4", "alice@x.test", "secret"); verify(users, never()).checkPassword(any(), eq("secret")); } }