diff --git a/src/main/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilter.kt b/src/main/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilter.kt index 704627dfc..bca34d009 100644 --- a/src/main/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilter.kt +++ b/src/main/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilter.kt @@ -37,16 +37,9 @@ class FirebaseTokenAuthenticationFilter( filterChain: FilterChain ) { verifyToken(request) - markVisit() filterChain.doFilter(request, response) } - private fun markVisit() { - if (SecurityContextHolder.getContext().authentication != null) { - userAccountService.markVisitForCurrentUser() - } - } - private fun verifyToken(request: HttpServletRequest) { val token: String? = tokenHelperUtils.getBearerToken(request) try { diff --git a/src/main/kotlin/com/epam/brn/auth/filter/RememberLastVisitFilter.kt b/src/main/kotlin/com/epam/brn/auth/filter/RememberLastVisitFilter.kt new file mode 100644 index 000000000..ef0a0da4a --- /dev/null +++ b/src/main/kotlin/com/epam/brn/auth/filter/RememberLastVisitFilter.kt @@ -0,0 +1,35 @@ +package com.epam.brn.auth.filter + +import com.epam.brn.service.UserAccountService +import org.apache.logging.log4j.kotlin.logger +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.stereotype.Component +import org.springframework.web.filter.OncePerRequestFilter +import javax.servlet.FilterChain +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse + +@Component +class RememberLastVisitFilter( + private val userAccountService: UserAccountService, +) : OncePerRequestFilter() { + + private val log = logger() + + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain + ) { + markVisit() + filterChain.doFilter(request, response) + } + + private fun markVisit() { + try { + if (SecurityContextHolder.getContext().authentication != null) userAccountService.markVisitForCurrentUser() + } catch (e: Exception) { + log.error("Error: ${e.message}", e) + } + } +} diff --git a/src/main/kotlin/com/epam/brn/config/WebSecurityBasicConfiguration.kt b/src/main/kotlin/com/epam/brn/config/WebSecurityBasicConfiguration.kt index e49725e0c..2e2afcc2b 100644 --- a/src/main/kotlin/com/epam/brn/config/WebSecurityBasicConfiguration.kt +++ b/src/main/kotlin/com/epam/brn/config/WebSecurityBasicConfiguration.kt @@ -1,6 +1,7 @@ package com.epam.brn.config import com.epam.brn.auth.filter.FirebaseTokenAuthenticationFilter +import com.epam.brn.auth.filter.RememberLastVisitFilter import com.epam.brn.enums.BrnRole import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @@ -21,7 +22,8 @@ import javax.servlet.http.HttpServletResponse @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true) class WebSecurityBasicConfiguration( - private val firebaseTokenAuthenticationFilter: FirebaseTokenAuthenticationFilter + private val firebaseTokenAuthenticationFilter: FirebaseTokenAuthenticationFilter, + private val rememberLastVisitFilter: RememberLastVisitFilter, ) : WebSecurityConfigurerAdapter() { @Throws(Exception::class) @@ -32,6 +34,7 @@ class WebSecurityBasicConfiguration( .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .addFilterBefore(firebaseTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java) + .addFilterAfter(rememberLastVisitFilter, UsernamePasswordAuthenticationFilter::class.java) .authorizeRequests() .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**").hasRole(BrnRole.ADMIN) .and() diff --git a/src/test/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilterTest.kt b/src/test/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilterTest.kt index 65e507d63..2fe3119c9 100644 --- a/src/test/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilterTest.kt +++ b/src/test/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilterTest.kt @@ -86,10 +86,10 @@ internal class FirebaseTokenAuthenticationFilterTest { every { firebaseAuth.verifyIdToken(token, true) } returns firebaseTokenMock every { firebaseTokenMock.email } returns email every { brainUpUserDetailsService.loadUserByUsername(email) } returns customUserDetailsMock - justRun { userAccountService.markVisitForCurrentUser() } // WHEN firebaseTokenAuthenticationFilter.doFilter(request, response, filterChain) + // THEN val authentication = SecurityContextHolder.getContext().authentication assertNotNull(authentication) @@ -100,7 +100,6 @@ internal class FirebaseTokenAuthenticationFilterTest { verify(exactly = 1) { tokenHelperUtils.getBearerToken(request) } verify(exactly = 1) { firebaseAuth.verifyIdToken(token, true) } verify(exactly = 1) { brainUpUserDetailsService.loadUserByUsername(email) } - verify(exactly = 1) { userAccountService.markVisitForCurrentUser() } verify(exactly = 0) { firebaseUserService.getUserByUuid(any()) } verify(exactly = 0) { userAccountService.createUser(any()) } } @@ -128,10 +127,10 @@ internal class FirebaseTokenAuthenticationFilterTest { gender = null, name = fullName ) - justRun { userAccountService.markVisitForCurrentUser() } // WHEN firebaseTokenAuthenticationFilter.doFilter(requestMock, responseMock, filterChain) + // THEN val authentication = SecurityContextHolder.getContext().authentication assertNotNull(authentication) @@ -144,7 +143,6 @@ internal class FirebaseTokenAuthenticationFilterTest { verify(exactly = 2) { brainUpUserDetailsService.loadUserByUsername(email) } verify(exactly = 1) { firebaseUserService.getUserByUuid(uuid) } verify(exactly = 1) { userAccountService.createUser(any()) } - verify(exactly = 1) { userAccountService.markVisitForCurrentUser() } } @Test @@ -157,9 +155,16 @@ internal class FirebaseTokenAuthenticationFilterTest { val filterChain = FilterChain { _, _ -> } every { tokenHelperUtils.getBearerToken(requestMock) } returns tokenMock - every { firebaseAuth.verifyIdToken(tokenMock, true) } throws (FirebaseAuthException(FirebaseException(ErrorCode.INVALID_ARGUMENT, "Token invalid", null))) + every { + firebaseAuth.verifyIdToken( + tokenMock, + true + ) + } throws (FirebaseAuthException(FirebaseException(ErrorCode.INVALID_ARGUMENT, "Token invalid", null))) + // WHEN firebaseTokenAuthenticationFilter.doFilter(requestMock, responseMock, filterChain) + // THEN val authentication = SecurityContextHolder.getContext().authentication assertNull(authentication) @@ -169,7 +174,6 @@ internal class FirebaseTokenAuthenticationFilterTest { verify(exactly = 0) { brainUpUserDetailsService.loadUserByUsername(any()) } verify(exactly = 0) { firebaseUserService.getUserByUuid(any()) } verify(exactly = 0) { userAccountService.createUser(any()) } - verify(exactly = 0) { userAccountService.markVisitForCurrentUser() } } @Test @@ -183,8 +187,10 @@ internal class FirebaseTokenAuthenticationFilterTest { every { tokenHelperUtils.getBearerToken(requestMock) } returns tokenMock every { firebaseAuth.verifyIdToken(tokenMock, true) } throws (IllegalArgumentException()) + // WHEN firebaseTokenAuthenticationFilter.doFilter(requestMock, responseMock, filterChain) + // THEN val authentication = SecurityContextHolder.getContext().authentication assertNull(authentication) @@ -194,7 +200,6 @@ internal class FirebaseTokenAuthenticationFilterTest { verify(exactly = 0) { brainUpUserDetailsService.loadUserByUsername(any()) } verify(exactly = 0) { firebaseUserService.getUserByUuid(any()) } verify(exactly = 0) { userAccountService.createUser(any()) } - verify(exactly = 0) { userAccountService.markVisitForCurrentUser() } } @Test @@ -216,6 +221,7 @@ internal class FirebaseTokenAuthenticationFilterTest { // WHEN firebaseTokenAuthenticationFilter.doFilter(requestMock, responseMock, filterChain) + // THEN val authentication = SecurityContextHolder.getContext().authentication assertNull(authentication) @@ -224,7 +230,6 @@ internal class FirebaseTokenAuthenticationFilterTest { verify(exactly = 1) { firebaseAuth.verifyIdToken(tokenMock, true) } verify(exactly = 1) { brainUpUserDetailsService.loadUserByUsername(email) } verify(exactly = 1) { firebaseUserService.getUserByUuid(uuid) } - verify(exactly = 0) { userAccountService.markVisitForCurrentUser() } verify(exactly = 0) { userAccountService.createUser(any()) } } diff --git a/src/test/kotlin/com/epam/brn/auth/filter/RememberLastVisitFilterTest.kt b/src/test/kotlin/com/epam/brn/auth/filter/RememberLastVisitFilterTest.kt new file mode 100644 index 000000000..77e14108a --- /dev/null +++ b/src/test/kotlin/com/epam/brn/auth/filter/RememberLastVisitFilterTest.kt @@ -0,0 +1,51 @@ +package com.epam.brn.auth.filter + +import com.epam.brn.service.UserAccountService +import io.mockk.impl.annotations.InjectMockKs +import io.mockk.impl.annotations.MockK +import io.mockk.junit5.MockKExtension +import io.mockk.justRun +import io.mockk.verify +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.springframework.http.HttpMethod +import org.springframework.mock.web.MockHttpServletRequest +import org.springframework.mock.web.MockHttpServletResponse +import org.springframework.security.core.context.SecurityContextHolder +import javax.servlet.FilterChain + +@ExtendWith(MockKExtension::class) +@DisplayName("RememberLastVisitFilter test using MockK") +internal class RememberLastVisitFilterTest { + + @InjectMockKs + lateinit var rememberLastVisitFilter: RememberLastVisitFilter + + @MockK + lateinit var userAccountService: UserAccountService + + @BeforeEach + fun init() { + SecurityContextHolder.clearContext() + } + + @Test + fun `should mark visit for current user`() { + // GIVEN + val requestMock = MockHttpServletRequest(HttpMethod.GET.name, "/test") + val tokenMock = "firebaseTokenMock" + requestMock.addHeader("Authorization", "Bearer $tokenMock") + val responseMock = MockHttpServletResponse() + val filterChain = FilterChain { _, _ -> } + + justRun { userAccountService.markVisitForCurrentUser() } + + // WHEN + rememberLastVisitFilter.doFilter(requestMock, responseMock, filterChain) + + // THEN + verify(exactly = 1) { userAccountService.markVisitForCurrentUser() } + } +}