diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 94116fe0a..cf24e9135 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -45,7 +45,7 @@ jobs: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_USER }} run: | git fetch --unshallow - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install sonar:sonar -Dsonar.projectKey=ita-social-projects-green-city-user -Dsonar.organization=ita-social-projects -Dsonar.host.url=https://sonarcloud.io -Dsonar.sources='src/main/java/greencity' -Dsonar.binaries=target/classes -Dsonar.dynamicAnalysis=reuseReports -Dsonar.coverage.exclusions=**/config/* + mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install sonar:sonar -Dsonar.projectKey=ita-social-projects-green-city-user -Dsonar.organization=ita-social-projects -Dsonar.host.url=https://sonarcloud.io -Dsonar.sources='src/main/java/greencity' -Dsonar.binaries=target/classes -Dsonar.dynamicAnalysis=reuseReports -Dsonar.coverage.exclusions=**/config/*,**/dto/security/* # Checks-out your greencity.repository under $GITHUB_WORKSPACE, so your job can access it - name: Test Reporter diff --git a/core/pom.xml b/core/pom.xml index e8ffd257a..eaa07a517 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -183,7 +183,20 @@ 1.9.20 + + org.springframework.cloud + spring-cloud-starter-openfeign + 4.1.3 + + + + org.springframework.cloud + spring-cloud-dependencies + 2023.0.3 + pom + + diff --git a/core/src/main/java/greencity/UserApplication.java b/core/src/main/java/greencity/UserApplication.java index 9ec1112c2..180d0b143 100644 --- a/core/src/main/java/greencity/UserApplication.java +++ b/core/src/main/java/greencity/UserApplication.java @@ -2,10 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling +@EnableFeignClients public class UserApplication { /** * Main method of SpringBoot app. diff --git a/core/src/main/java/greencity/config/SecurityConfig.java b/core/src/main/java/greencity/config/SecurityConfig.java index 01a3788ce..7b4c53ce1 100644 --- a/core/src/main/java/greencity/config/SecurityConfig.java +++ b/core/src/main/java/greencity/config/SecurityConfig.java @@ -124,7 +124,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .requestMatchers(HttpMethod.POST, "/ownSecurity/signUp", "/ownSecurity/signIn", - "/ownSecurity/updatePassword") + "/ownSecurity/updatePassword", + "/ownSecurity/unblockAccount") .permitAll() .requestMatchers(HttpMethod.GET, "/user/shopping-list-items/habits/{habitId}/shopping-list", diff --git a/core/src/main/java/greencity/exception/handler/CustomExceptionHandler.java b/core/src/main/java/greencity/exception/handler/CustomExceptionHandler.java index 77b3c33f3..87e94a8f0 100644 --- a/core/src/main/java/greencity/exception/handler/CustomExceptionHandler.java +++ b/core/src/main/java/greencity/exception/handler/CustomExceptionHandler.java @@ -15,6 +15,8 @@ import greencity.exception.exceptions.PasswordsDoNotMatchesException; import greencity.exception.exceptions.UserAlreadyHasPasswordException; import greencity.exception.exceptions.UserAlreadyRegisteredException; +import greencity.exception.exceptions.UserBlockedException; +import greencity.exception.exceptions.WrongCaptchaException; import greencity.exception.exceptions.WrongEmailException; import greencity.exception.exceptions.WrongIdException; import greencity.exception.exceptions.WrongPasswordException; @@ -499,4 +501,38 @@ public ResponseEntity handleBase64DecodedException(Base64DecodedExceptio return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(exceptionResponse); } + + /** + * Handles exceptions of type {@link UserBlockedException}. + * + * @param exception the UserBlockedException instance + * @param request the current web request + * @return a ResponseEntity containing the HTTP status code and error response + * body + */ + @ExceptionHandler(UserBlockedException.class) + public ResponseEntity handleUserBlockedException(UserBlockedException exception, + WebRequest request) { + log.error(exception.getMessage()); + ExceptionResponse exceptionResponse = new ExceptionResponse(getErrorAttributes(request)); + + return ResponseEntity.status(HttpStatus.LOCKED).body(exceptionResponse); + } + + /** + * Handles exceptions of type {@link WrongCaptchaException}. + * + * @param exception the WrongCaptchaException instance + * @param request the current web request + * @return a ResponseEntity containing the HTTP status code and error response + * body + */ + @ExceptionHandler(WrongCaptchaException.class) + public ResponseEntity handleWrongCaptchaException(WrongCaptchaException exception, + WebRequest request) { + log.error(exception.getMessage()); + ExceptionResponse exceptionResponse = new ExceptionResponse(getErrorAttributes(request)); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(exceptionResponse); + } } diff --git a/core/src/main/java/greencity/security/controller/OwnSecurityController.java b/core/src/main/java/greencity/security/controller/OwnSecurityController.java index bc85dd370..6cd5a01df 100644 --- a/core/src/main/java/greencity/security/controller/OwnSecurityController.java +++ b/core/src/main/java/greencity/security/controller/OwnSecurityController.java @@ -22,6 +22,7 @@ import greencity.security.dto.ownsecurity.OwnSignUpDto; import greencity.security.dto.ownsecurity.PasswordStatusDto; import greencity.security.dto.ownsecurity.SetPasswordDto; +import greencity.security.dto.ownsecurity.UnblockAccountDto; import greencity.security.dto.ownsecurity.UpdatePasswordDto; import greencity.security.service.OwnSecurityService; import greencity.security.service.PasswordRecoveryService; @@ -298,4 +299,22 @@ public ResponseEntity deleteUser() { service.deleteUserByEmail(email); return ResponseEntity.ok().build(); } + + /** + * Unblocks user account by provided token. + * + * @param token {@link String} token for unblocking user account. + * @return {@link ResponseEntity} with 200 status if unblocking is successful. + */ + @Operation(summary = "Unblock user account") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = HttpStatuses.OK), + @ApiResponse(responseCode = "400", description = HttpStatuses.BAD_REQUEST), + @ApiResponse(responseCode = "404", description = ErrorMessage.USER_NOT_FOUND_BY_EMAIL) + }) + @PostMapping("/unblockAccount") + public ResponseEntity unblockAccount(@RequestBody UnblockAccountDto token) { + service.unblockAccount(token.token()); + return ResponseEntity.ok().build(); + } } diff --git a/core/src/main/resources/application-dev.properties b/core/src/main/resources/application-dev.properties index 1fb5b7f22..b65fb3476 100644 --- a/core/src/main/resources/application-dev.properties +++ b/core/src/main/resources/application-dev.properties @@ -64,4 +64,12 @@ greencityubs.server.address = http://localhost:8050 greencitychat.server.address = ${CHAT_LINK} #Swagger -springdoc.swagger-ui.doc-expansion=none \ No newline at end of file +springdoc.swagger-ui.doc-expansion=none + +#BruteForceSettings +bruteForceSettings.maxAttempts=${MAX_ATTEMPTS:5} +bruteForceSettings.blockTimeInHours=${BLOCK_TIME_IN_HOURS:1} +bruteForceSettings.blockTimeInMinutes=${BLOCK_TIME_IN_MINUTES:15} + +#CloudFlare +cloud-flare.secret-key=${CLOUD_FLARE_SECRET_KEY:0x4AAAAAAAxJcrEzmJE2s9LRh2ftN4vIby0} diff --git a/core/src/main/resources/application-docker.properties b/core/src/main/resources/application-docker.properties index 2138b1187..a3caf6b17 100644 --- a/core/src/main/resources/application-docker.properties +++ b/core/src/main/resources/application-docker.properties @@ -66,3 +66,11 @@ greencitychat.server.address = ${CHAT_LINK} #Swagger springdoc.swagger-ui.doc-expansion=none + +#BruteForceSettings +bruteForceSettings.maxAttempts=${MAX_ATTEMPTS:5} +bruteForceSettings.blockTimeInHours=${BLOCK_TIME_IN_HOURS:1} +bruteForceSettings.blockTimeInMinutes=${BLOCK_TIME_IN_MINUTES:15} + +#CloudFlare +cloud-flare.secret-key=${CLOUD_FLARE_SECRET_KEY:0x4AAAAAAAxJcrEzmJE2s9LRh2ftN4vIby0} diff --git a/core/src/main/resources/application-prod.properties b/core/src/main/resources/application-prod.properties index e54ce03b0..86bdb65fd 100644 --- a/core/src/main/resources/application-prod.properties +++ b/core/src/main/resources/application-prod.properties @@ -76,4 +76,12 @@ spring.web.resources.static-locations=classpath:/static/ greencity.authorization.googleApiKey=${GOOGLE_API_KEY:default-key} #Swagger -springdoc.swagger-ui.doc-expansion=none \ No newline at end of file +springdoc.swagger-ui.doc-expansion=none + +#BruteForceSettings +bruteForceSettings.maxAttempts=${MAX_ATTEMPTS:5} +bruteForceSettings.blockTimeInHours=${BLOCK_TIME_IN_HOURS:1} +bruteForceSettings.blockTimeInMinutes=${BLOCK_TIME_IN_MINUTES:15} + +#CloudFlare +cloud-flare.secret-key=${CLOUD_FLARE_SECRET_KEY:0x4AAAAAAAxJcrEzmJE2s9LRh2ftN4vIby0} \ No newline at end of file diff --git a/core/src/main/resources/messages_en.properties b/core/src/main/resources/messages_en.properties index 6065227ce..dc2e8020b 100644 --- a/core/src/main/resources/messages_en.properties +++ b/core/src/main/resources/messages_en.properties @@ -42,6 +42,15 @@ sincerely.yours.greenCity=Sincerely yours, Green City team. sincerely.yours.Ubs=Sincerely yours, Pick Up City team. unsubscribe.text=If you no longer wish to receive these emails, you can unsubscribe=unsubscribe +unlock=Unlock account +block.account=Account blocked +text.account.ban=Your account has been locked for security reasons due to a possible hacking attempt. For your safety, we recommend changing your password after restoring your account. +warning=Warning! +block.user=Your account is blocked +security.alert=Security Alert: Account Locked +condition.restore=If the restore button does not work, just paste this link into your browser: +condition.unblock=If the unblock button does not work, just paste this link into your browser: +advice.for.block=You received this email for security reasons. To protect your account, we recommend changing your password after unlocking your account. profile.text=If you no longer wish to receive these emails, you can unsubscribe from them in your profile=profile read.more=READ MORE \ No newline at end of file diff --git a/core/src/main/resources/messages_uk.properties b/core/src/main/resources/messages_uk.properties index dc3140db1..0f136ba5e 100644 --- a/core/src/main/resources/messages_uk.properties +++ b/core/src/main/resources/messages_uk.properties @@ -42,6 +42,15 @@ sincerely.yours.greenCity=\u0429\u0438\u0440\u043e\u0020\u0432\u0430\u0448\u0430 sincerely.yours.Ubs=\u0429\u0438\u0440\u043e\u0020\u0432\u0430\u0448\u0430\u002c\u0020\u043a\u043e\u043c\u0430\u043d\u0434\u0430\u0020\u0050\u0069\u0063\u006b\u0020\u0055\u0070\u0020\u0043\u0069\u0074\u0079\u002e unsubscribe.text=\u042f\u043a\u0449\u043e \u0432\u0438 \u0431\u0456\u043b\u044c\u0448\u0435 \u043d\u0435 \u0445\u043e\u0447\u0435\u0442\u0435 \u043e\u0442\u0440\u0438\u043c\u0443\u0432\u0430\u0442\u0438 \u0440\u043e\u0437\u0441\u0438\u043b\u043a\u0443\u002c \u0432\u0438 \u043c\u043e\u0436\u0435\u0442\u0435 unsubscribe=\u0432\u0456\u0434\u043f\u0438\u0441\u0430\u0442\u0438\u0441\u044f +unlock=\u0420\u043E\u0437\u0431\u043B\u043E\u043A\u0443\u0432\u0430\u0442\u0438\u0020\u043E\u0431\u043B\u0456\u043A\u043E\u0432\u0438\u0439\u0020\u0437\u0430\u043F\u0438\u0441 +block.account=\u041E\u0431\u043B\u0456\u043A\u043E\u0432\u0438\u0439\u0020\u0437\u0430\u043F\u0438\u0441\u0020\u0437\u0430\u0431\u043B\u043E\u043A\u043E\u0432\u0430\u043D\u043E +text.account.ban=\u0412\u0430\u0448\u0020\u043E\u0431\u043B\u0456\u043A\u043E\u0432\u0438\u0439\u0020\u0437\u0430\u043F\u0438\u0441\u0020\u0437\u0430\u0431\u043B\u043E\u043A\u043E\u0432\u0430\u043D\u043E\u0020\u0437\u0020\u043C\u0456\u0440\u043A\u0443\u0432\u0430\u043D\u044C\u0020\u0431\u0435\u0437\u043F\u0435\u043A\u0438\u0020\u0447\u0435\u0440\u0435\u0437\u0020\u043C\u043E\u0436\u043B\u0438\u0432\u0443\u0020\u0441\u043F\u0440\u043E\u0431\u0443\u0020\u0437\u043B\u043E\u043C\u0443\u002E\u0020\u0414\u043B\u044F\u0020\u0432\u0430\u0448\u043E\u0457\u0020\u0431\u0435\u0437\u043F\u0435\u043A\u0438\u0020\u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0454\u043C\u043E\u0020\u0437\u043C\u0456\u043D\u0438\u0442\u0438\u0020\u043F\u0430\u0440\u043E\u043B\u044C\u0020\u043F\u0456\u0441\u043B\u044F\u0020\u0432\u0456\u0434\u043D\u043E\u0432\u043B\u0435\u043D\u043D\u044F\u0020\u043E\u0431\u043B\u0456\u043A\u043E\u0432\u043E\u0433\u043E\u0020\u0437\u0430\u043F\u0438\u0441\u0443\u002E +warning=\u0423\u0432\u0430\u0433\u0430\u0021\u000D\u000A +block.user=\u0412\u0430\u0448\u0020\u043E\u0431\u043B\u0456\u043A\u043E\u0432\u0438\u0439\u0020\u0437\u0430\u043F\u0438\u0441\u0020\u0437\u0430\u0431\u043B\u043E\u043A\u043E\u0432\u0430\u043D\u043E +security.alert=\u0421\u043F\u043E\u0432\u0456\u0449\u0435\u043D\u043D\u044F\u0020\u0431\u0435\u0437\u043F\u0435\u043A\u0438\u003A\u0020\u043E\u0431\u043B\u0456\u043A\u043E\u0432\u0438\u0439\u0020\u0437\u0430\u043F\u0438\u0441\u0020\u0437\u0430\u0431\u043B\u043E\u043A\u043E\u0432\u0430\u043D\u043E +condition.restore=\u042F\u043A\u0449\u043E\u0020\u043A\u043D\u043E\u043F\u043A\u0430\u0020\u0432\u0456\u0434\u043D\u043E\u0432\u043B\u0435\u043D\u043D\u044F\u0020\u043D\u0435\u0020\u043F\u0440\u0430\u0446\u044E\u0454\u002C\u0020\u043F\u0440\u043E\u0441\u0442\u043E\u0020\u0432\u0441\u0442\u0430\u0432\u0442\u0435\u0020\u0446\u0435\u0020\u043F\u043E\u0441\u0438\u043B\u0430\u043D\u043D\u044F\u0020\u0443\u0020\u0441\u0432\u0456\u0439\u0020\u0431\u0440\u0430\u0443\u0437\u0435\u0440\u003A +condition.unblock=\u042F\u043A\u0449\u043E\u0020\u043A\u043D\u043E\u043F\u043A\u0430\u0020\u0440\u043E\u0437\u0431\u043B\u043E\u043A\u0443\u0432\u0430\u043D\u043D\u044F\u0020\u043D\u0435\u0020\u043F\u0440\u0430\u0446\u044E\u0454\u002C\u0020\u043F\u0440\u043E\u0441\u0442\u043E\u0020\u0432\u0441\u0442\u0430\u0432\u0442\u0435\u0020\u0446\u0435\u0020\u043F\u043E\u0441\u0438\u043B\u0430\u043D\u043D\u044F\u0020\u0443\u0020\u0441\u0432\u0456\u0439\u0020\u0431\u0440\u0430\u0443\u0437\u0435\u0440\u003A +advice.for.block=\u0412\u0438\u0020\u043E\u0442\u0440\u0438\u043C\u0430\u043B\u0438\u0020\u0446\u0435\u0439\u0020\u0435\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u0438\u0439\u0020\u043B\u0438\u0441\u0442\u0020\u0437\u0020\u043C\u0456\u0440\u043A\u0443\u0432\u0430\u043D\u044C\u0020\u0431\u0435\u0437\u043F\u0435\u043A\u0438\u002E\u0020\u0429\u043E\u0431\u0020\u0437\u0430\u0445\u0438\u0441\u0442\u0438\u0442\u0438\u0020\u0441\u0432\u0456\u0439\u0020\u043E\u0431\u043B\u0456\u043A\u043E\u0432\u0438\u0439\u0020\u0437\u0430\u043F\u0438\u0441\u002C\u0020\u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0454\u043C\u043E\u0020\u0437\u043C\u0456\u043D\u0438\u0442\u0438\u0020\u043F\u0430\u0440\u043E\u043B\u044C\u0020\u043F\u0456\u0441\u043B\u044F\u0020\u0440\u043E\u0437\u0431\u043B\u043E\u043A\u0443\u0432\u0430\u043D\u043D\u044F\u0020\u043E\u0431\u043B\u0456\u043A\u043E\u0432\u043E\u0433\u043E\u0020\u0437\u0430\u043F\u0438\u0441\u0443\u002E profile.text=\u042f\u043a\u0449\u043e\u0020\u0432\u0438\u0020\u0431\u0456\u043b\u044c\u0448\u0435\u0020\u043d\u0435\u0020\u0445\u043e\u0447\u0435\u0442\u0435\u0020\u043e\u0442\u0440\u0438\u043c\u0443\u0432\u0430\u0442\u0438\u0020\u0440\u043e\u0437\u0441\u0438\u043b\u043a\u0443\u002c\u0020\u0432\u0438\u0020\u043c\u043e\u0436\u0435\u0442\u0435\u0020\u0432\u0456\u0434\u043f\u0438\u0441\u0430\u0442\u0438\u0441\u044f\u0020\u0432\u0456\u0434\u0020\u043d\u0435\u0457\u0020\u0443\u0020\u0441\u0432\u043e\u0454\u043c\u0443 profile=\u043f\u0440\u043e\u0444\u0456\u043b\u0456 read.more=\u0427\u0418\u0422\u0410\u0422\u0418 \u0414\u0410\u041b\u0406 \ No newline at end of file diff --git a/core/src/main/resources/templates/core/management_login.html b/core/src/main/resources/templates/core/management_login.html index 94d6931f8..e203f3f8b 100644 --- a/core/src/main/resources/templates/core/management_login.html +++ b/core/src/main/resources/templates/core/management_login.html @@ -45,60 +45,62 @@ } + - - - - - З поверненням! - - Будь ласка, внеси свої дані для - входу + - - - - - Електронна пошта - - Validation error - - - Пароль - - Validation error - - - - - або + + + З поверненням! + + Будь ласка, внеси свої дані для + входу + + + + + + Електронна пошта + + Validation error + + + Пароль + + Validation error + + + + + + або - - - - Увійти через - Google + + + + Увійти через + Google + - - + + - -
Validation error