diff --git a/captcha/src/main/kotlin/ru/touchin/captcha/aspects/CaptchaSiteVerifyAspect.kt b/captcha/src/main/kotlin/ru/touchin/captcha/aspects/CaptchaSiteVerifyAspect.kt index 9c4ebe7..778643c 100644 --- a/captcha/src/main/kotlin/ru/touchin/captcha/aspects/CaptchaSiteVerifyAspect.kt +++ b/captcha/src/main/kotlin/ru/touchin/captcha/aspects/CaptchaSiteVerifyAspect.kt @@ -2,16 +2,17 @@ package ru.touchin.captcha.aspects import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Before +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.stereotype.Component import org.springframework.web.context.request.RequestContextHolder import org.springframework.web.context.request.ServletRequestAttributes import ru.touchin.captcha.annotations.Captcha import ru.touchin.captcha.exceptions.CaptchaResponseMissingException import ru.touchin.captcha.services.CaptchaService -import java.lang.IllegalStateException @Aspect @Component +@ConditionalOnProperty(prefix = "captcha", name = ["enabled"], havingValue = "true", matchIfMissing = true) class CaptchaSiteVerifyAspect(private val captchaService: CaptchaService) { @Throws(Throwable::class) @@ -21,7 +22,7 @@ class CaptchaSiteVerifyAspect(private val captchaService: CaptchaService) { as? ServletRequestAttributes ?: throw IllegalStateException("unable to get current request attributes") - val captchaResponse = currentRequestAttributes.request.getHeader(CAPTCHA_HEADER_NAME) + val captchaResponse = currentRequestAttributes.request.getHeader(CAPTCHA_RESPONSE_HEADER_NAME) ?: throw CaptchaResponseMissingException() captchaService.verify(captchaResponse) @@ -31,7 +32,7 @@ class CaptchaSiteVerifyAspect(private val captchaService: CaptchaService) { companion object { - private const val CAPTCHA_HEADER_NAME = "X-Captcha-Response" + private const val CAPTCHA_RESPONSE_HEADER_NAME = "X-Captcha-Response" } diff --git a/captcha/src/main/kotlin/ru/touchin/captcha/configurations/CaptchaConfiguration.kt b/captcha/src/main/kotlin/ru/touchin/captcha/configurations/CaptchaConfiguration.kt new file mode 100644 index 0000000..9e0043a --- /dev/null +++ b/captcha/src/main/kotlin/ru/touchin/captcha/configurations/CaptchaConfiguration.kt @@ -0,0 +1,8 @@ +package ru.touchin.captcha.configurations + +import org.springframework.boot.context.properties.ConfigurationPropertiesScan +import org.springframework.context.annotation.ComponentScan + +@ComponentScan("ru.touchin.captcha") +@ConfigurationPropertiesScan("ru.touchin.captcha") +class CaptchaConfiguration diff --git a/exception-handler-spring-security-web/build.gradle.kts b/exception-handler-spring-security-web/build.gradle.kts new file mode 100644 index 0000000..5369ae2 --- /dev/null +++ b/exception-handler-spring-security-web/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("kotlin") + id("kotlin-spring") + id("maven-publish") +} + +dependencies { + implementation(project(":exception-handler-spring-web")) + + implementation("org.springframework.boot:spring-boot-starter-security") + + testImplementation("org.springframework.boot:spring-boot-starter-test") +} diff --git a/exception-handler-spring-security-web/src/main/kotlin/ru/touchin/exception/handler/spring/security/EnableSpringSecurityExceptionHandler.kt b/exception-handler-spring-security-web/src/main/kotlin/ru/touchin/exception/handler/spring/security/EnableSpringSecurityExceptionHandler.kt new file mode 100644 index 0000000..f44c23a --- /dev/null +++ b/exception-handler-spring-security-web/src/main/kotlin/ru/touchin/exception/handler/spring/security/EnableSpringSecurityExceptionHandler.kt @@ -0,0 +1,8 @@ +@file:Suppress("unused") +package ru.touchin.exception.handler.spring.security + +import org.springframework.context.annotation.Import +import ru.touchin.exception.handler.spring.security.configurations.SecurityExceptionHandlerConfiguration + +@Import(value = [SecurityExceptionHandlerConfiguration::class]) +annotation class EnableSpringSecurityExceptionHandler diff --git a/exception-handler-spring-security-web/src/main/kotlin/ru/touchin/exception/handler/spring/security/configurations/SecurityExceptionHandlerConfiguration.kt b/exception-handler-spring-security-web/src/main/kotlin/ru/touchin/exception/handler/spring/security/configurations/SecurityExceptionHandlerConfiguration.kt new file mode 100644 index 0000000..06ed137 --- /dev/null +++ b/exception-handler-spring-security-web/src/main/kotlin/ru/touchin/exception/handler/spring/security/configurations/SecurityExceptionHandlerConfiguration.kt @@ -0,0 +1,6 @@ +package ru.touchin.exception.handler.spring.security.configurations + +import org.springframework.context.annotation.ComponentScan + +@ComponentScan("ru.touchin.exception.handler.spring.security.resolvers") +class SecurityExceptionHandlerConfiguration diff --git a/exception-handler-spring-security-web/src/main/kotlin/ru/touchin/exception/handler/spring/security/resolvers/AccessDeniedExceptionResolver.kt b/exception-handler-spring-security-web/src/main/kotlin/ru/touchin/exception/handler/spring/security/resolvers/AccessDeniedExceptionResolver.kt new file mode 100644 index 0000000..ea000cc --- /dev/null +++ b/exception-handler-spring-security-web/src/main/kotlin/ru/touchin/exception/handler/spring/security/resolvers/AccessDeniedExceptionResolver.kt @@ -0,0 +1,32 @@ +package ru.touchin.exception.handler.spring.security.resolvers + +import org.springframework.core.annotation.Order +import org.springframework.http.HttpStatus +import org.springframework.security.access.AccessDeniedException +import org.springframework.stereotype.Component +import ru.touchin.common.spring.Ordered +import ru.touchin.common.spring.web.dto.DefaultApiError +import ru.touchin.exception.handler.dto.ExceptionResolverResult +import ru.touchin.exception.handler.spring.resolvers.ExceptionResolver + +@Order(Ordered.LOW) +@Component +class AccessDeniedExceptionResolver : ExceptionResolver { + + override fun invoke(exception: Exception): ExceptionResolverResult? { + if (exception is AccessDeniedException) { + return createAccessDeniedError(exception) + } + + return null + } + + private fun createAccessDeniedError(exception: Exception?): ExceptionResolverResult { + return ExceptionResolverResult( + apiError = DefaultApiError.createFailure(exception?.message), + status = HttpStatus.FORBIDDEN, + exception = exception, + ) + } + +} diff --git a/exception-handler-spring-security-web/src/test/kotlin/ru/touchin/exception/handler/TestApplication.kt b/exception-handler-spring-security-web/src/test/kotlin/ru/touchin/exception/handler/TestApplication.kt new file mode 100644 index 0000000..d4163e7 --- /dev/null +++ b/exception-handler-spring-security-web/src/test/kotlin/ru/touchin/exception/handler/TestApplication.kt @@ -0,0 +1,8 @@ +package ru.touchin.exception.handler + +import org.springframework.boot.autoconfigure.SpringBootApplication +import ru.touchin.exception.handler.spring.EnableSpringExceptionHandler + +@SpringBootApplication +@EnableSpringExceptionHandler +class TestApplication diff --git a/exception-handler-spring-security-web/src/test/kotlin/ru/touchin/exception/handler/spring/advices/ExceptionHandlerAdviceMvcTest.kt b/exception-handler-spring-security-web/src/test/kotlin/ru/touchin/exception/handler/spring/advices/ExceptionHandlerAdviceMvcTest.kt new file mode 100644 index 0000000..06b3011 --- /dev/null +++ b/exception-handler-spring-security-web/src/test/kotlin/ru/touchin/exception/handler/spring/advices/ExceptionHandlerAdviceMvcTest.kt @@ -0,0 +1,44 @@ +package ru.touchin.exception.handler.spring.advices + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/api/errors") +class ErrorController { + + + @GetMapping("/unauthorized") + fun runtimeError(): String { + return "ok" + } + +} + +@ActiveProfiles("test") +@SpringBootTest +@AutoConfigureMockMvc +internal class ExceptionHandlerAdviceMvcTest { + + @Autowired + lateinit var mockMvc: MockMvc + + @Test + @DisplayName("Should be unauthorized") + fun shouldGetUnauthorized() { + mockMvc + .perform(get("/api/errors/unauthorized")) + .andExpect(status().isUnauthorized) + } + +} diff --git a/exception-handler-spring-web/build.gradle.kts b/exception-handler-spring-web/build.gradle.kts index 1bbdb21..74988d5 100644 --- a/exception-handler-spring-web/build.gradle.kts +++ b/exception-handler-spring-web/build.gradle.kts @@ -12,7 +12,6 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.springframework.boot:spring-boot-starter-web") - implementation("org.springframework.boot:spring-boot-starter-security") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin") diff --git a/exception-handler-spring-web/src/main/kotlin/ru/touchin/exception/handler/dto/ExceptionResolverResult.kt b/exception-handler-spring-web/src/main/kotlin/ru/touchin/exception/handler/dto/ExceptionResolverResult.kt index 2b87ee0..67c3608 100644 --- a/exception-handler-spring-web/src/main/kotlin/ru/touchin/exception/handler/dto/ExceptionResolverResult.kt +++ b/exception-handler-spring-web/src/main/kotlin/ru/touchin/exception/handler/dto/ExceptionResolverResult.kt @@ -39,14 +39,6 @@ data class ExceptionResolverResult( ) } - fun createAccessDeniedError(exception: Exception?): ExceptionResolverResult { - return ExceptionResolverResult( - apiError = DefaultApiError.createFailure(exception?.message), - status = HttpStatus.FORBIDDEN, - exception = exception, - ) - } - } } diff --git a/exception-handler-spring-web/src/main/kotlin/ru/touchin/exception/handler/spring/resolvers/AccessDeniedExceptionResolver.kt b/exception-handler-spring-web/src/main/kotlin/ru/touchin/exception/handler/spring/resolvers/AccessDeniedExceptionResolver.kt deleted file mode 100644 index 42630b1..0000000 --- a/exception-handler-spring-web/src/main/kotlin/ru/touchin/exception/handler/spring/resolvers/AccessDeniedExceptionResolver.kt +++ /dev/null @@ -1,22 +0,0 @@ -package ru.touchin.exception.handler.spring.resolvers - -import org.springframework.core.annotation.Order -import org.springframework.security.access.AccessDeniedException -import org.springframework.stereotype.Component -import ru.touchin.common.exceptions.CommonNotFoundException -import ru.touchin.common.spring.Ordered -import ru.touchin.exception.handler.dto.ExceptionResolverResult - -@Order(Ordered.LOW) -@Component -class AccessDeniedExceptionResolver : ExceptionResolver { - - override fun invoke(exception: Exception): ExceptionResolverResult? { - if (exception is AccessDeniedException) { - return ExceptionResolverResult.createAccessDeniedError(exception) - } - - return null - } - -} diff --git a/gradle.properties b/gradle.properties index ede854d..0e52839 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official -springBootVersion=2.4.1 +springBootVersion=2.5.3 springDependencyManagementVersion=1.0.11.RELEASE -kotlinVersion=1.4.32 +kotlinVersion=1.5.21 diff --git a/logger-spring-web/src/main/kotlin/ru/touchin/logger/spring/web/configurations/SpringLoggerWebConfiguration.kt b/logger-spring-web/src/main/kotlin/ru/touchin/logger/spring/web/configurations/SpringLoggerWebConfiguration.kt new file mode 100644 index 0000000..874947a --- /dev/null +++ b/logger-spring-web/src/main/kotlin/ru/touchin/logger/spring/web/configurations/SpringLoggerWebConfiguration.kt @@ -0,0 +1,6 @@ +package ru.touchin.logger.spring.web.configurations + +import org.springframework.context.annotation.ComponentScan + +@ComponentScan("ru.touchin.logger.spring.web") +class SpringLoggerWebConfiguration diff --git a/logger-spring-web/src/main/kotlin/ru/touchin/logger/spring/web/webclients/DefaultWebClientLogger.kt b/logger-spring-web/src/main/kotlin/ru/touchin/logger/spring/web/webclients/DefaultWebClientLogger.kt new file mode 100644 index 0000000..f12621a --- /dev/null +++ b/logger-spring-web/src/main/kotlin/ru/touchin/logger/spring/web/webclients/DefaultWebClientLogger.kt @@ -0,0 +1,36 @@ +package ru.touchin.logger.spring.web.webclients + +import org.springframework.stereotype.Component +import ru.touchin.common.spring.web.webclient.dto.RequestLogData +import ru.touchin.common.spring.web.webclient.logger.WebClientLogger +import ru.touchin.logger.dto.LogData +import ru.touchin.logger.factory.LogBuilderFactory + +@Component +class DefaultWebClientLogger( + private val logBuilderFactory: LogBuilderFactory +) : WebClientLogger { + + override fun log(requestLogData: RequestLogData) { + logBuilderFactory.create(this::class.java) + .addTags(*requestLogData.logTags.toTypedArray()) + .setMethod(requestLogData.method.toString()) + .addData("uri" to requestLogData.uri) + .also { builder -> + requestLogData.requestBody?.also { + builder.addData( + "requestBody" to it, + ) + } + + requestLogData.responseBody?.also { + builder.addData( + "responseData" to it, + ) + } + } + .build() + .log() + } + +} diff --git a/logger-spring/build.gradle.kts b/logger-spring/build.gradle.kts index 0da95c0..cad07d1 100644 --- a/logger-spring/build.gradle.kts +++ b/logger-spring/build.gradle.kts @@ -9,6 +9,8 @@ dependencies { annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") + implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation(project(":common-spring")) diff --git a/logger-spring/src/main/kotlin/ru/touchin/logger/spring/serializers/resolvers/ObjectLogValueResolverImpl.kt b/logger-spring/src/main/kotlin/ru/touchin/logger/spring/serializers/resolvers/ObjectLogValueResolverImpl.kt index fe14ca2..e549e11 100644 --- a/logger-spring/src/main/kotlin/ru/touchin/logger/spring/serializers/resolvers/ObjectLogValueResolverImpl.kt +++ b/logger-spring/src/main/kotlin/ru/touchin/logger/spring/serializers/resolvers/ObjectLogValueResolverImpl.kt @@ -3,6 +3,7 @@ package ru.touchin.logger.spring.serializers.resolvers import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import org.springframework.core.annotation.Order import org.springframework.stereotype.Component import ru.touchin.common.spring.Ordered @@ -13,6 +14,7 @@ import ru.touchin.logger.spring.serializers.resolvers.dto.ResolvedValue class ObjectLogValueResolverImpl : LogValueResolver { private val objectMapper = ObjectMapper() + .registerModule(JavaTimeModule()) .setSerializationInclusion(JsonInclude.Include.NON_NULL) override operator fun invoke(value: Any): ResolvedValue { diff --git a/logger-spring/src/test/kotlin/ru/touchin/logger/serializers/impl/LogValueFieldResolverImplTest.kt b/logger-spring/src/test/kotlin/ru/touchin/logger/serializers/impl/LogValueFieldResolverImplTest.kt index ce6c10c..df162a4 100644 --- a/logger-spring/src/test/kotlin/ru/touchin/logger/serializers/impl/LogValueFieldResolverImplTest.kt +++ b/logger-spring/src/test/kotlin/ru/touchin/logger/serializers/impl/LogValueFieldResolverImplTest.kt @@ -2,6 +2,7 @@ package ru.touchin.logger.serializers.impl import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.DisplayName @@ -25,6 +26,7 @@ internal class LogValueFieldResolverImplTest { private lateinit var logValueFieldSerializer: LogValueFieldSerializer private val objectMapper = ObjectMapper() + .registerModule(JavaTimeModule()) .setSerializationInclusion(JsonInclude.Include.NON_NULL) @Suppress("unused") diff --git a/settings.gradle.kts b/settings.gradle.kts index 96ead46..969cabb 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -36,6 +36,7 @@ include("logger") include("logger-spring") include("logger-spring-web") include("exception-handler-spring-web") +include("exception-handler-spring-security-web") include("exception-handler-logger-spring-web") include("validation-spring") include("version-spring-web")