From e57d1974998096c89784eb0a6fa6b3bd953db0be Mon Sep 17 00:00:00 2001 From: Sheikah45 Date: Sun, 18 Jul 2021 12:29:07 -0400 Subject: [PATCH 1/7] Add throttle page --- .../config/SecurityConfiguration.kt | 1 + .../userservice/controller/OAuthController.kt | 9 +++++ .../userservice/domain/UserService.kt | 10 +++++- src/main/resources/i18n/messages.properties | 1 + src/main/resources/templates/login.html | 2 +- src/main/resources/templates/throttle.html | 33 +++++++++++++++++++ .../UserServiceApplicationTests.kt | 2 +- 7 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 src/main/resources/templates/throttle.html diff --git a/src/main/kotlin/com/faforever/userservice/config/SecurityConfiguration.kt b/src/main/kotlin/com/faforever/userservice/config/SecurityConfiguration.kt index 7a396b49..00044ef1 100644 --- a/src/main/kotlin/com/faforever/userservice/config/SecurityConfiguration.kt +++ b/src/main/kotlin/com/faforever/userservice/config/SecurityConfiguration.kt @@ -28,6 +28,7 @@ class SecurityConfiguration { authorize("/favicon.ico", permitAll) authorize("/login", permitAll) authorize("/consent", permitAll) + authorize("/throttle", permitAll) authorize("/css/**", permitAll) authorize("/js/**", permitAll) authorize("/**", authenticated) diff --git a/src/main/kotlin/com/faforever/userservice/controller/OAuthController.kt b/src/main/kotlin/com/faforever/userservice/controller/OAuthController.kt index bf0eaccc..38091309 100644 --- a/src/main/kotlin/com/faforever/userservice/controller/OAuthController.kt +++ b/src/main/kotlin/com/faforever/userservice/controller/OAuthController.kt @@ -114,4 +114,13 @@ class OAuthController( }.flatMap { redirectUrl -> redirect(response, redirectUrl) } + + @GetMapping("throttle") + fun showThrottle( + request: ServerHttpRequest, + @RequestParam("login_challenge") challenge: String, + model: Model, + ): Mono { + return Mono.just(Rendering.view("throttle").build()) + } } diff --git a/src/main/kotlin/com/faforever/userservice/domain/UserService.kt b/src/main/kotlin/com/faforever/userservice/domain/UserService.kt index d28096e7..9fe299c4 100644 --- a/src/main/kotlin/com/faforever/userservice/domain/UserService.kt +++ b/src/main/kotlin/com/faforever/userservice/domain/UserService.kt @@ -8,6 +8,7 @@ import org.springframework.boot.context.properties.ConstructorBinding import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.stereotype.Component import org.springframework.validation.annotation.Validated +import org.springframework.web.util.UriComponentsBuilder import reactor.core.publisher.Mono import reactor.kotlin.core.publisher.switchIfEmpty import sh.ory.hydra.model.AcceptConsentRequest @@ -91,7 +92,14 @@ class UserService( .flatMap { throttlingRequired -> if (throttlingRequired) { hydraService.rejectLoginRequest(challenge, GenericError(HYDRA_ERROR_LOGIN_THROTTLED)) - .map { LoginResult.LoginThrottlingActive(it.redirectTo) } + .map { + LoginResult.LoginThrottlingActive( + UriComponentsBuilder.fromUriString("/throttle") + .queryParam("login_challenge", challenge) + .build() + .toUriString() + ) + } } else { hydraService.getLoginRequest(challenge) .flatMap { loginRequest -> diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties index e84918d3..0bf7df95 100644 --- a/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -2,6 +2,7 @@ header.loggedInAs=You are logged in as: {0} login.title=FAForever Login login.welcomeBack=Welcome back, Commander! login.badCredentials=Username or password was wrong +login.throttled=Too many failed attempts please wait before trying again login.usernameOrEmail=Username or email login.password=Password login.loginAction=Log in diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index 104f9536..1db4fbda 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -31,7 +31,7 @@

Welcome back, commande
- Username or password was wrong. + Username or password was wrong
diff --git a/src/main/resources/templates/throttle.html b/src/main/resources/templates/throttle.html new file mode 100644 index 00000000..840e6114 --- /dev/null +++ b/src/main/resources/templates/throttle.html @@ -0,0 +1,33 @@ + + + + FAForever Login + + + + + + + +
+
+
+ FAForever Logo +

FAForever Login

+
+
+ +
+
+
+
+
+
FAForever Logo
+

Too many failed attempts. Please wait before trying again

+
+
+
+ + diff --git a/src/test/kotlin/com/faforever/userservice/UserServiceApplicationTests.kt b/src/test/kotlin/com/faforever/userservice/UserServiceApplicationTests.kt index 55d061de..a48f329e 100644 --- a/src/test/kotlin/com/faforever/userservice/UserServiceApplicationTests.kt +++ b/src/test/kotlin/com/faforever/userservice/UserServiceApplicationTests.kt @@ -162,7 +162,7 @@ class UserServiceApplicationTests { .exchange() .expectStatus().is3xxRedirection() .expectHeader() - .location(hydraRedirectUrl) + .location(String.format("/throttle?login_challenge=%s", challenge)) .expectBody(String::class.java) verify(loginLogRepository).findFailedAttemptsByIp(anyString()) From c4f869eb8136bca519d51119b4ee35a4ebe109e9 Mon Sep 17 00:00:00 2001 From: Sheikah45 Date: Sun, 18 Jul 2021 19:29:34 -0400 Subject: [PATCH 2/7] Use an error on login page for throttling --- .../userservice/controller/OAuthController.kt | 20 +++++------ .../userservice/domain/UserService.kt | 14 ++------ src/main/resources/templates/login.html | 5 +++ src/main/resources/templates/throttle.html | 33 ------------------- 4 files changed, 18 insertions(+), 54 deletions(-) delete mode 100644 src/main/resources/templates/throttle.html diff --git a/src/main/kotlin/com/faforever/userservice/controller/OAuthController.kt b/src/main/kotlin/com/faforever/userservice/controller/OAuthController.kt index 38091309..8ecf5bbc 100644 --- a/src/main/kotlin/com/faforever/userservice/controller/OAuthController.kt +++ b/src/main/kotlin/com/faforever/userservice/controller/OAuthController.kt @@ -34,7 +34,9 @@ class OAuthController( model: Model, ): Mono { val loginFailed = request.queryParams.containsKey("login_failed") + val loginThrottled = request.queryParams.containsKey("login_throttled") model.addAttribute("loginFailed", loginFailed) + model.addAttribute("loginThrottled", loginThrottled) model.addAttribute("challenge", challenge) model.addAttribute("passwordResetUrl", fafProperties.passwordResetUrl) model.addAttribute("registerAccountUrl", fafProperties.registerAccountUrl) @@ -64,7 +66,14 @@ class OAuthController( when (it) { is SuccessfulLogin -> redirect(response, it.redirectTo) is UserBanned -> redirect(response, it.redirectTo) - is LoginThrottlingActive -> redirect(response, it.redirectTo) + is LoginThrottlingActive -> redirect( + response, + UriComponentsBuilder.fromUri(request.uri) + .queryParam("login_challenge", challenge) + .queryParam("login_throttled") + .build() + .toUriString() + ) is UserOrCredentialsMismatch -> redirect( response, UriComponentsBuilder.fromUri(request.uri) @@ -114,13 +123,4 @@ class OAuthController( }.flatMap { redirectUrl -> redirect(response, redirectUrl) } - - @GetMapping("throttle") - fun showThrottle( - request: ServerHttpRequest, - @RequestParam("login_challenge") challenge: String, - model: Model, - ): Mono { - return Mono.just(Rendering.view("throttle").build()) - } } diff --git a/src/main/kotlin/com/faforever/userservice/domain/UserService.kt b/src/main/kotlin/com/faforever/userservice/domain/UserService.kt index 9fe299c4..24864046 100644 --- a/src/main/kotlin/com/faforever/userservice/domain/UserService.kt +++ b/src/main/kotlin/com/faforever/userservice/domain/UserService.kt @@ -28,7 +28,7 @@ data class SecurityProperties( ) sealed class LoginResult { - data class LoginThrottlingActive(val redirectTo: String) : LoginResult() + object LoginThrottlingActive : LoginResult() object UserOrCredentialsMismatch : LoginResult() data class SuccessfulLogin(val redirectTo: String) : LoginResult() data class UserBanned(val redirectTo: String, val ban: Ban) : LoginResult() @@ -69,7 +69,7 @@ class UserService( ) { val lastAttempt = it.lastAttemptAt!! if (LocalDateTime.now().minusMinutes(securityProperties.failedLoginThrottlingMinutes) - .isBefore(lastAttempt) + .isBefore(lastAttempt) ) { log.debug("IP '$ip' is trying again to early -> throttle it") true @@ -91,15 +91,7 @@ class UserService( ): Mono = checkLoginThrottlingRequired(ip) .flatMap { throttlingRequired -> if (throttlingRequired) { - hydraService.rejectLoginRequest(challenge, GenericError(HYDRA_ERROR_LOGIN_THROTTLED)) - .map { - LoginResult.LoginThrottlingActive( - UriComponentsBuilder.fromUriString("/throttle") - .queryParam("login_challenge", challenge) - .build() - .toUriString() - ) - } + Mono.just(LoginResult.LoginThrottlingActive) } else { hydraService.getLoginRequest(challenge) .flatMap { loginRequest -> diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index 1db4fbda..4bd698f7 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -34,6 +34,11 @@

Welcome back, commande Username or password was wrong +
+ + Too many failed attempts please wait before trying again +
+ diff --git a/src/main/resources/templates/throttle.html b/src/main/resources/templates/throttle.html deleted file mode 100644 index 840e6114..00000000 --- a/src/main/resources/templates/throttle.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - FAForever Login - - - - - - - -
-
-
- FAForever Logo -

FAForever Login

-
-
- -
-
-
-
-
-
FAForever Logo
-

Too many failed attempts. Please wait before trying again

-
-
-
- - From 48148468414d313783a344f5fa197a6317e96788 Mon Sep 17 00:00:00 2001 From: Sheikah45 Date: Sun, 18 Jul 2021 19:42:27 -0400 Subject: [PATCH 3/7] spotlessApply --- .../kotlin/com/faforever/userservice/domain/UserService.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/kotlin/com/faforever/userservice/domain/UserService.kt b/src/main/kotlin/com/faforever/userservice/domain/UserService.kt index 24864046..ba75f504 100644 --- a/src/main/kotlin/com/faforever/userservice/domain/UserService.kt +++ b/src/main/kotlin/com/faforever/userservice/domain/UserService.kt @@ -8,7 +8,6 @@ import org.springframework.boot.context.properties.ConstructorBinding import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.stereotype.Component import org.springframework.validation.annotation.Validated -import org.springframework.web.util.UriComponentsBuilder import reactor.core.publisher.Mono import reactor.kotlin.core.publisher.switchIfEmpty import sh.ory.hydra.model.AcceptConsentRequest @@ -69,7 +68,7 @@ class UserService( ) { val lastAttempt = it.lastAttemptAt!! if (LocalDateTime.now().minusMinutes(securityProperties.failedLoginThrottlingMinutes) - .isBefore(lastAttempt) + .isBefore(lastAttempt) ) { log.debug("IP '$ip' is trying again to early -> throttle it") true From 2844d4c9869be6f955303d9cdca36fd9dfcb9210 Mon Sep 17 00:00:00 2001 From: Sheikah45 Date: Sun, 18 Jul 2021 19:47:58 -0400 Subject: [PATCH 4/7] fix tests --- .../com/faforever/userservice/UserServiceApplicationTests.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/kotlin/com/faforever/userservice/UserServiceApplicationTests.kt b/src/test/kotlin/com/faforever/userservice/UserServiceApplicationTests.kt index a48f329e..517689d6 100644 --- a/src/test/kotlin/com/faforever/userservice/UserServiceApplicationTests.kt +++ b/src/test/kotlin/com/faforever/userservice/UserServiceApplicationTests.kt @@ -162,7 +162,7 @@ class UserServiceApplicationTests { .exchange() .expectStatus().is3xxRedirection() .expectHeader() - .location(String.format("/throttle?login_challenge=%s", challenge)) + .location("/login?login_challenge=someChallenge&login_challenge=someChallenge&login_throttled") .expectBody(String::class.java) verify(loginLogRepository).findFailedAttemptsByIp(anyString()) From 6e2d962aa1da20a564ae11e3629feb5f989d50d5 Mon Sep 17 00:00:00 2001 From: Sheikah45 Date: Mon, 19 Jul 2021 07:44:52 -0400 Subject: [PATCH 5/7] Update throttled message --- src/main/resources/i18n/messages.properties | 2 +- src/main/resources/templates/login.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties index 0bf7df95..42c10d97 100644 --- a/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -2,7 +2,7 @@ header.loggedInAs=You are logged in as: {0} login.title=FAForever Login login.welcomeBack=Welcome back, Commander! login.badCredentials=Username or password was wrong -login.throttled=Too many failed attempts please wait before trying again +login.throttled=Too many of your login attempts have failed. Please wait some time before trying to login again. login.usernameOrEmail=Username or email login.password=Password login.loginAction=Log in diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index 4bd698f7..c0bff4a7 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -36,7 +36,7 @@

Welcome back, commande
- Too many failed attempts please wait before trying again + Too many of your login attempts have failed. Please wait some time before trying to login again.
From 73434385e5474422edd53373a61bd718f1cc357c Mon Sep 17 00:00:00 2001 From: Sheikah45 Date: Mon, 19 Jul 2021 18:41:00 -0400 Subject: [PATCH 6/7] remove unneeded security mapping --- .../com/faforever/userservice/config/SecurityConfiguration.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/com/faforever/userservice/config/SecurityConfiguration.kt b/src/main/kotlin/com/faforever/userservice/config/SecurityConfiguration.kt index 00044ef1..7a396b49 100644 --- a/src/main/kotlin/com/faforever/userservice/config/SecurityConfiguration.kt +++ b/src/main/kotlin/com/faforever/userservice/config/SecurityConfiguration.kt @@ -28,7 +28,6 @@ class SecurityConfiguration { authorize("/favicon.ico", permitAll) authorize("/login", permitAll) authorize("/consent", permitAll) - authorize("/throttle", permitAll) authorize("/css/**", permitAll) authorize("/js/**", permitAll) authorize("/**", authenticated) From 4ba501628d5ac5a2f3c4268c84907e726b7b8d4f Mon Sep 17 00:00:00 2001 From: Brutus5000 Date: Tue, 20 Jul 2021 21:39:43 +0200 Subject: [PATCH 7/7] Add german throttled translation --- src/main/resources/i18n/messages_de.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/i18n/messages_de.properties b/src/main/resources/i18n/messages_de.properties index 3c6bcaa7..4f15fb97 100644 --- a/src/main/resources/i18n/messages_de.properties +++ b/src/main/resources/i18n/messages_de.properties @@ -2,6 +2,7 @@ header.loggedInAs=Du bist eingeloggt als: {0} login.title=FAForever Login login.welcomeBack=Willkommen zurück, Commander! login.badCredentials=Benutzername oder Passwort falsch +login.throttled=Zu viele deiner Login-Versuche sind fehlgeschlagen. Bitte warte einige Zeit bevor du es erneut versuchst. login.usernameOrEmail=Benutzername oder Email login.password=Passwort login.loginAction=Einloggen