#StackBounty: #reactjs #spring #file-saver Cannot open downloaded zip file from rest endpoint using file-saver

Bounty: 50

Here is the backend code for the download endpoint:

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ZipOutputStream zipOut = new ZipOutputStream(byteArrayOutputStream);
for (Long id : ids) {
    // Get the "generated" file using the id    
    zipOut.putNextEntry(new ZipEntry(generated.getName() + ".doc"));
    InputStream inputStream = new ByteArrayInputStream(generated.getFile().getBytes(1, (int)generated.getFile().length()));
    IOUtils.copy(inputStream, zipOut);
    zipOut.closeEntry();
}
zipOut.close();

response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=File.zip");

byte[] zipBytes = byteArrayOutputStream.toByteArray();
OutputStream outputStream = response.getOutputStream();
outputStream.write(zipBytes);
outputStream.close();
response.flushBuffer();

And for the frontend, I am using axios and file-saver

import { saveAs } from "file-saver";

request.then((response: any) => {
  const blob = new Blob([response.data], { type: "application/zip" });
  saveAs(blob, "Report.zip");
});

I can download the zip file, but when I tried to open, I got the follwing error:

"An attempt was made to move the file pointer before the beginning of the file"


Get this bounty!!!

#StackBounty: #java #spring #spring-boot #mocking #integration-testing Spring Boot Integration Testing – Mocking @Service before Applic…

Bounty: 50

I have to create a integration test for a microservice X which downloads, processes and importing csv files from external sftp servers. The whole process is started by a spring boot scheduler task which starts a spring batch job for processing and importing the data. The import process is done by the spring batch writer, which is a restTemplate Repository (so it calls post requests to another microservice Y).

I already managed to mock the sftp server, putting a test file on it and the current integration test is downloading the file. (https://github.com/stefanbirkner/fake-sftp-server-rule/)

My problem is, that the task will be scheduled immediately when the application context starts so there is no trigger like a api call. To get the whole integration test working i have to mock the part where the external microservice Y is called through a restTemplate call. This repository is called in the spring batch writer and this repository is created by a repositoryFactory which is a @Service. The repositoryFactory is injected in the spring batch configuration class.

I already tried to use the @MockBean annotation in the test class as well as in a separate test configuration where i am mocking the create() function of the factory to deliver a repository mock. But at some point it does not work and it delivers still the original object which leads to interupt the import job.

I also tried to use the WireMock library, but also in this case it does not catched any api calls and at some point leads to interrupt the sftp socket. (?)

I hope someone could help me out.

The current test:

@NoArgsConstructor
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(classes = {JsonHalConfig.class})
@TestPropertySource(locations = "classpath:application-test.properties")
@TestMethodOrder(MethodOrderer.MethodName.class)
public class ImportIT {

    @ClassRule
    public static final FakeSftpServerRule sftpServer = new FakeSftpServerRule();
    private static final String PASSWORD = "password";
    private static final String USER = "username";
    private static final int PORT = 14022;

    @BeforeClass
    public static void beforeClass() throws IOException {
        URL resource = getTestResource();
        if (resource != null) {
            sftpServer.setPort(PORT).addUser(USER, PASSWORD);
            sftpServer.createDirectories("/home/username/it-space/IMPORT", "/home/username/it-space/EXPORT");
            sftpServer.putFile("/home/username/it-space/IMPORT/INBOX/testFile.csv",
                    resource.openStream());
        } else {
            throw new IOException("Failed to get test resources");
        }
    }

    private static URL getTestResource() {
        return ImportIT.class.getClassLoader().getResource("testFile.csv");
    }

    @Test
    public void test_A_() throws IOException, RepositoryException {
        assertTrue(true);
    }
}

I tried following configuration classes

(included in @ContextConfiguration)

@Configuration/@TestConfiguration
public class RepositoryTestConfig {
    @Bean
    @Primary
    public IRepositoryFactory repositoryFactory() {
        IRepositoryFactory repositoryFactory = mock(IRepositoryFactory.class);
        IRepository repository = mock(IRepository.class);
        when(repositoryFactory.create(anyString())).thenReturn(repository);
        return repositoryFactory;
    }
}

(as static class in the test class)

    @TestConfiguration/@Configuration
    public static class RepositoryTestConfig {
        @MockBean
        private IRepositoryFactory repositoryFactory;

        @PostConstruct
        public void initMock(){
            IRepository repository = mock(IRepository.class);
            Mockito.when(repositoryFactory.create(anyString())).thenReturn(
                    repository
            );
        }
    }

UPDATE 27.08.2021
I have a RestConfig @Component where a new RestTemplateBuilder is created. I tried to @MockBean this component to deliver a RestTemplateBuilder Mock and injected a MockRestServiceServer object to catch outgoing api calls. But unfortunately it does not work as aspected. Am i missing something? I also tried to create a "TestRestController" to trigger the scheduling of the task but it never delivers the mock…


Get this bounty!!!

#StackBounty: #java #spring #spring-boot #spring-security #spring-security-oauth2 Lazy initialise spring security at runtime + reload s…

Bounty: 50

Spring usually eagerly loading the spring security configuration while starting the application. I’m using OAuth with Spring Security

I’m maintaining a configuration table for storing the SSO related values (like jwk-url, client_id, client_secret). This values will be populated by an admin user via CRUD in the same spring boot application.

Then only the jwk-url is available to be configure in the Spring security configuration (refer below code - jwkSetUri(...)). This would not available at the application startup.

So I wanted to initialise the spring security configuration after the value is loaded into the table, like a lazy loading (@Lazy) at runtime. I know how to do Lazy loading of a regular class/service.

  1. But still I’m not sure how to invoke the configure(HttpSecurity http) method at runtime and how to p
    ass the HttpSecurity parameter. When I just try invoke new ResourceServerConfiguration() like a lazy loading at runtime, I don’t see the configure() method is called. (Or) this class needs to be maintained as bean and lazy load whenever needed. But still not sure about how to call configure() in code.
  2. Another thing is how to refresh/reload the spring security configuration at runtime, if the JWK url is changed by admin. Then only the spring security configuration can take effect of the changes.
@Configuration
@EnableWebSecurity
public class ResourceServerConfiguration extends WebSecurityConfigurerAdapter {
    
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.cors()
                .and()
                .csrf().disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .oauth2ResourceServer()
                .authenticationEntryPoint(oAuth2AuthenticationEntryPoint)
                .accessDeniedHandler(oAuth2AccessDeniedHandler)
                .jwt()
                 // Some Auth server URL which would be fetch from table
                .jwkSetUri(ssoConfigService.getActiveSSOCertificateURL()); 
                 // Eg. http://localhost:8090/auth/realms/demo-app/protocol/openid-connect/certs
    }
}

I have already referred these links. But it doesn’t help for my purpose. Any help would be appreciated.

How do I lazy load Spring Security?

How to reload the Configure method of WebSecurityConfigurerAdapter when the application is up and running

Modify Spring Security Config at Runtime

Configure Spring HTTP Security at Runtime


Get this bounty!!!

#StackBounty: #java #spring #junit4 #testcontainers Spring @Sql Annotations, possible to run once before all tests?

Bounty: 250

Using Spring for integration tests I am able to populate a test db running scripts like so…

  @Test
   @Sql({"/db/schema.sql", "/db/accountConfig.sql", "/db/functions/fnSomething.sql"})
  public void verifySomething() {
....

However I’d like to run all my .sql files only once before any test runs. Is there a junit 4 way to do this? It seems like @Sql only runs for methods with the @Test annotations.

I’m using Junit 4, Spring Boot, Java 15, testcontainers.

Things I’ve tried…

  • I’ve tried using @BeforeClass on a class my test classes extend but that seems to run after my tests.
  • Testcontainers does have a initscript function but it only takes one file, not ideal.
  • I’ve also tried ScriptUtils.executeSqlScript but for some reason test containers does not like that.

Here is my sample code that works with @SQL but does not work with ScriptUtils.executeSqlScript.

@ContextConfiguration(initializers = AbstractIntegrationTest.Initializer.class)
public abstract class AbstractIntegrationTest {

  @ClassRule public static MSSQLServerContainer mssqlserver = new MSSQLServerContainer();

  
  public static class Initializer
      implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
      ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
      Properties props = new Properties();

      props.put("spring.datasource.driver-class-name", mssqlserver.getDriverClassName());
      props.put("spring.datasource.url", mssqlserver.getJdbcUrl());
      props.put("spring.datasource.username", mssqlserver.getUsername());
      props.put("spring.datasource.password", mssqlserver.getPassword());

      environment
          .getPropertySources()
          .addFirst(new PropertiesPropertySource("myTestDBProps", props));
      configurableApplicationContext.setEnvironment(environment);
      
       
    }
  }

My test classes simply extends AbstractIntegrationTest. But using @SQL runs scripts for every test case. Anyone have a suggestion for a better way to init sql scripts? Tried flyway but it won’t allow a creation of a db from a script.


Get this bounty!!!

#StackBounty: #spring #spring-boot #spring-data-jpa Override entityManager factories mapping at conditionally at runtime

Bounty: 50

I have a multi tenant/DB application implemented using an AbstractRoutingDataSource. Some of the DBs have a slightly different schema and missing some columns in some tables (this is static and known for each DB). Instead of duplicating all repositories and entities for each DB schema I would like to just mark missing columns as transient if the DB does not have the columns (but still save all information if the columns are available).

I was able to override the annotation based mappings in the entity manager factory using an XML based mapping file which I could create for all possible schemas. My idea is to create an entity manager factory for each tenant with the appropriate XML mapping override. Ideally on the first request of a tenant it will instantiate the entity manager factory and then check what mappings override to apply. Pseudo code:

@Configuration
@EnableTransactionManagement
class JPAconfig {

  @Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource myAbstractRoutingDataSource, TenantService tenantService) {
    final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource(myAbstractRoutingDataSource);
    em.setPackagesToScan("myPackagesToScan");
    final JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    em.setJpaVendorAdapter(vendorAdapter);
    em.setJpaProperties(additionalProperties());
    String schemaVer = env.getProperty("db.schema.version");

    // At runtime based on the tenant, override annotation based mappings
    em.setMappingResources(tenantService.getMappingsForTenant());
    
    return em;
  }
}

Unfortunately LocalContainerEntityManagerFactoryBean is a factory bean and only seems to allow singleton or prototype scope. It would also be okay to define all possible LocalContainerEntityManagerFactoryBean beans at compile time and somehow select the correct factory based on the tenant at runtime.


Get this bounty!!!

#StackBounty: #spring #spring-security #authorization #abac How to integrate ABAC in a Spring application?

Bounty: 50

Spring Security offers means for authorization like annotations (@PreAuthorize) and security-specific SpEL expression like hasRole. In principle this expression language could be extended to support ABAC, however that wouldn’t really solve the architectural problem where to do it.

The application architecture is inspired by the Clean Architecture (which is in turn inspired by Hexagonal Architecture and the Onion Architecture). The basic building blocks look like this:

  • Adapter: connects to the outside world, e. g. HTTP endpoints
  • Application services: transforms to and from DTOs, provide transactions, perform authorization
  • Domain: describes business data, rules and behaviours

From my point of view the application layer is the only suitable layer to perform authorization. It wouldn’t fit into the adapter layer, because there could be multiple adapters for a single application service, so that the authorization would have to be repeated in several places. It wouldn’t obviously fit into the domain, because it is a technical aspect that shouldn’t be part of the domain logic.

A typical application service method gets some more or less "raw" parameters from the client, converts it and delegate the execution to the domain:

@Transactional
class MyApplicationService {
    fun cancelOrder(id: Long) {
        val orderId = OrderId(orderId)
        val order = orderRepository.findById(orderId)
        order.cancel()
    }
}

In order to be able to perform attribute-based access control (ABAC), the order object must be loaded before, so that the attributes can be extracted in the first place. That does also mean that an annotation like @PreAuthorize above the cancelOrder function wouldn’t work, because the order object wouldn’t be available there (SpEL would be able to reference function parameters, but the order is not passed as parameter!).

The only remaining possibility, that I can think of, is to call an authorization function explicitly before proceeding with business logic:

    fun cancelOrder(id: Long, principal: Principal) {
        val orderId = OrderId(id)
        val order = orderRepository.findById(orderId)
        checkPolicy(principal, order, Context.Complaint) // ?
        order.cancel()
    }

What concrete ABAC server (like OPA, AuthzForce, etc.) is not yet decided, but the solution at this level should be independent from it.

Is there a common approach for what I outlined as checkPolicy? Is there a typical "Spring approach" to integrate an external policy decision point? Or is there a fundamentally better solution?


Get this bounty!!!

#StackBounty: #java #spring #spring-boot #spring-mvc #spring-test Spring Boot test Filter

Bounty: 50

I’ve been trying a couple different approaches to testing a Filter however I’m continuously getting one error or another so I’m hoping for some direction.

Here’s a dummy Filter that should just do a redirect.


package org.example.filters;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class RedirectFilter implements Filter {
  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) req;
    chain.sendRedirect("/splash");
  }
}

And a basic Spring Boot Application class:

package org.example;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ApplicationClass {

}

I think I’m just not clear on what ‘level’ of Spring Boot Test I’m trying to do, here’s some attempts I’ve done and the errors:

Option 1. Trying with mocks, for example from here

package org.example.filters;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
import org.junit.jupiter.api.Test;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

public class LocaleFilterIntegrationTestStandalone {

  @Test
  public void whenNoLocaleRedirectToSplash() throws Exception {
    standaloneSetup(new TestController()).addFilters(new RedirectFilter()).build().perform(get("/"))
        .andExpect(status().isFound()).andExpect(redirectedUrl("/splash"));
  }

  @Controller
  private static class TestController {
    @GetMapping("/")
    public String get() {
      return "got it";
    }
  }
}

Error: java.lang.ClassCastException: class org.springframework.mock.web.MockHttpServletRequest cannot be cast to class javax.servlet.http.HttpServletResponse

Option 2, try using @WebMvcTest, which has the same issue as Option 1

package org.example.filters;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.stereotype.Controller;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.GetMapping;

@WebMvcTest
public class LocaleFilterIntegrationTestWebMvc {
  @Autowired
  private MockMvc mvc;

  @Test
  public void noLanguageShouldRedirectToSplash() throws Exception {
    mvc.perform(get("/")).andExpect(status().isFound()).andExpect(redirectedUrl("/splash"));
  }

  @Controller
  private static class TestController {
    @GetMapping("/")
    public String get() {
      return "got it";
    }
  }
}

Error: java.lang.ClassCastException: class org.springframework.mock.web.MockHttpServletRequest cannot be cast to class javax.servlet.http.HttpServletResponse

Option 3, try booting the whole context, which I think is required to be able to cast to HttpServletRequest:

package org.example.filters;

import java.net.URI;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class LocaleFilterIntegrationTest {
  @LocalServerPort
  private int port;

  @Autowired
  private TestRestTemplate restTemplate;

  @Test
  public void noLanguageShouldRedirectToSplash() throws Exception {
    URI uri = new URI("http", "localhost:" + port, "/", null, null);
    String result = restTemplate.getForObject(uri.toString(), String.class);
    // not sure how, but test for redirect...
  }

  @Controller
  private static class TestController {
    @GetMapping("/")
    public String get() {
      return "got it";
    }
  }
}

Error: class org.apache.catalina.connector.RequestFacade cannot be cast to class javax.servlet.http.HttpServletResponse

Option 4, suggested by @m-deinem

package org.example.filters;

import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;

public class LocaleFilterIntegrationTestPlain {
  private final RedirectFilter redirectFilter = new RedirectFilter();

  @Test
  public void noLanguageShouldRedirectToSplash() throws Exception {
    MockHttpServletRequest req = new MockHttpServletRequest();
    MockHttpServletResponse res = new MockHttpServletResponse();
    MockFilterChain chain = new MockFilterChain();
    redirectFilter.doFilter(req, res, chain);
  }
}

Error: java.lang.ClassCastException: class org.springframework.mock.web.MockHttpServletRequest cannot be cast to class javax.servlet.http.HttpServletResponse


Get this bounty!!!

#StackBounty: #java #spring #spring-boot Spring Boot 2.3 Ambiguous Mapping

Bounty: 50

I have a spring controller with two endpoints that are returning an arbitrary method exception when they are accessed. I am trying to distinguish between them using the HeaderContentNegotiationStrategy which looks at the Accept header of the incoming request to determine which method to map the request to. To my understanding this strategy should compare the incoming accept header with the produces notation. In my case both methods do produce an application/json media type, however Spring also lets you provide a consumes which to my reading of their doc, should also have the content negotiation look at the request’s Content-Type header to map the method.

My question is, how do I get the content negotiation to look at the Content-Type header as well when doing this mapping? If you cannot get the content negotiator to look at the consumes notation, why is it there? Whats the point?

Controller:

@ResourcePayloadOverride(action = ActionTypes.READ)
@CustomPermission(Permission.READ)
@RequestMapping(method = RequestMethod.POST,
        produces = { MediaType.APPLICATION_JSON_VALUE, ResourceCollection.MEDIA_TYPE_JSON_VALUE },
        consumes = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<ResourceCollection<Class1>> method1(
        @PathVariable(value="param_1") String param1,
        @RequestParam(value="param_2", required=false) long param2,
        @RequestParam(value="param_3", required=false) int param3,
        @RequestParam(value="param_4", required=false) String param4,
        @RequestBody(required=false) String body) {
    [...]
}

@RequestMapping(method = { RequestMethod.POST },
        produces = { MediaType.APPLICATION_JSON_VALUE, Class1.MEDIA_TYPE_JSON_VALUE },
        consumes = { Class1.MEDIA_TYPE_JSON_VALUE, Class2.MEDIA_TYPE_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<Class1> post(
        @PathVariable("param1") String param1,
        @RequestParam(value="param2", required = false) String param2,
        @RequestParam(value="param3", required = false) String param3,
        @RequestParam(value="param4", required = false) String param4,
        @RequestBody(required=false) String noop) //Actually needed!!!
{
    [...]
}

Error:

{"version":1,"timeStamp":"2021-05-13T16:30:33.785Z","level":"error","source":"","message":"java.lang.IllegalStateException: Ambiguous handler methods mapped for '/{my_endpoint}': {public org.springframework.http.ResponseEntity {myPackage.class}.method1(java.lang.String,long,int,java.lang.String,java.lang.String), public org.springframework.http.ResponseEntity {myPackage.class}.post(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String)}","messageParameters":{"0":"java.lang.IllegalStateException","1":"Ambiguous handler methods mapped for '/{my_endpoint}': {public org.springframework.http.ResponseEntity {myPackage.class}.method1(java.lang.String,long,int,java.lang.String,java.lang.String), public org.springframework.http.ResponseEntity {myPackage.class).post(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String)}"

I’ve replaced a number of details with dummy values here but I dont believe this should effect the larger question


Get this bounty!!!

#StackBounty: #authentication #kotlin #spring #jwt Signing in with "Remember me" option implemented with Spring and Kotlin

Bounty: 50

I’m trying to implement authentication with "Remember me" option using Spring and Kotlin. I would really appriciate if someone could help me find potential issues with that implementation as well as improvements to the code.

Let’s start with sign in API. It accepts 3 parameters, email: String, password: String and stayLoggedIn: Boolean.

    @RestController
    @RequestMapping("/api/auth")
    internal class AuthApi(private val signInService: SignInService) {
        @PostMapping("/sign-in")
        fun signIn(@RequestBody request: SignInRequest, response: HttpServletResponse): ResponseEntity<Void> {
            val tokens = signInService.signIn(request)
            response.addCookie(tokens.accessToken.cookie)
            response.addCookie(tokens.refreshToken.cookie)
            return ResponseEntity(HttpStatus.OK)
        }
    }

As you can see, I’m not returning any response, instead tokens are stored in HttpOnly cookies, so frontend can’t access them directly (XSS protection). Now, the service itself is a bit more complex. It performs standard validation of email and password and then based on stayLoggedIn flag, it may save or override generated refresh_token and userId in database.

@Service
class SignInService(
    private val userRepository: UserRepository,
    private val tokenRepository: RefreshTokenRepository,
    private val tokenUtils: JwtTokenUtils,
    private val passwordEncoder: PasswordEncoder
) {

    fun signIn(request: SignInRequest): AccessTokenDto {
        return userRepository.findByEmail(request.email)
            ?.let { user -> authenticate(request, user) }
            ?: throw EmailNotFound(request.email)
    }


    private fun authenticate(request: SignInRequest, user: User): AccessTokenDto {
        when (validPassword(request.password, user.password)) {
            true -> {
                val refreshToken = tokenUtils.generateRefreshToken(user.id)
                val response = AccessTokenDto(
                    TokenCookie.accessTokenCookie(tokenUtils.generateAccessToken(user.id)),
                    TokenCookie.refreshTokenCookie(refreshToken)
                )
                if (request.stayLoggedIn) saveUserRefreshToken(user, refreshToken)
                return response
            }
            false -> throw IncorrectPassword()
        }
    }

    private fun saveUserRefreshToken(user: User, refreshToken: String) {
        tokenRepository.findByUserId(user.id)?.let {
            tokenRepository.save(it.copy(token = refreshToken))
        } ?: run {
            tokenRepository.save(RefreshToken(userId = user.id, token = refreshToken))
        }
    }

    private fun validPassword(providedPassword: String, actualPassword: String) =
        passwordEncoder.matches(providedPassword, actualPassword)
}

Code for TokenCookie looks like that

enum class TokenType(val value: String) {
    ACCESS_TOKEN("access_token"), REFRESH_TOKEN("refresh_token")
}

class TokenCookie(name: String, token: String) {
    val cookie: Cookie = Cookie(name, token)

    init {
        this.cookie.isHttpOnly = true
        this.cookie.path = "/"
    }

    companion object {
        fun accessTokenCookie(token: String) = TokenCookie(ACCESS_TOKEN.value, token)
        fun refreshTokenCookie(token: String) = TokenCookie(REFRESH_TOKEN.value, token)
    }
}

Let’s move on to verification that is performed for each request that is not authentication request !request.requestURI.startsWith("/api/auth")

First of all, validation function that produces 3 possible outcomes.
SUCCESS if everything is fine, EXPIRED if token is valid but has expired and INVALID for each other scenario

fun validateToken(authToken: String?, tokenType: TokenType = ACCESS_TOKEN): TokenValidationResult {
    return try {
        val claims = Jwts.parser().setSigningKey(tokenSecret).parseClaimsJws(authToken)
        if (claims.body["type"] == tokenType.value) SUCCESS else INVALID
    } catch (ex: UnsupportedJwtException) {
        INVALID
    } catch (ex: MalformedJwtException) {
        INVALID
    } catch (ex: IllegalArgumentException) {
        INVALID
    } catch (ex: SignatureException) {
        INVALID
    } catch (ex: ExpiredJwtException) {
        EXPIRED
    }
}

Now, the heart of whole security implementation, JwtTokenFilter.

  1. First of all, it tries to find access_token in request cookies
  2. Then it performs validation of that token.
  • In case of success, it tries to extend this token duration, which happens only if this request contained valid refresh_token as well. Main goal for this is to avoid situation when user logged in without selecting "Remember me" option and he uses my application for over 15 minutes (which is access_token expiration time). Without extending this token, after this period of time user will need to provide credentials again.
  • In case of expired access_token, job is delegated to ExpiredTokenHandler that I’ll show a bit later. Long story short, it tries to find refresh_token stored in database (which happens only if user selected option "Remember me"). If that token is there and it matches token provided with HTTP request, than both tokens are extended.
  • In case of invalid access_token, I’m not doing anything, which means I’m not storing any Authentication in SecurityContext, therefore next Spring Filter will block access to the system.
@Component
internal class JwtTokenFilter(
    private val tokenUtils: JwtTokenUtils,
    private val expiredTokenHandler: ExpiredTokenHandler
) : OncePerRequestFilter() {
    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
        if (!request.requestURI.startsWith("/api/auth")) {
            validateTokens(request, response)
        }
        filterChain.doFilter(request, response)
    }
 
    private fun validateTokens(
        request: HttpServletRequest,
        response: HttpServletResponse
    ) {
        getTokenFromCookies(request, ACCESS_TOKEN)?.let {
            val tokenValidationResult = tokenUtils.validateToken(it.value)
            val userId = tokenUtils.getId(it.value)
            if (tokenValidationResult == SUCCESS) {
                updateSecurityContext(userId)
                extendAccessToken(request, response, userId)
            } else if (tokenValidationResult == EXPIRED) {
                val refreshToken = getTokenFromCookies(request, REFRESH_TOKEN)?.value
                if (expiredTokenHandler.checkRefreshEligibility(response, it.value, refreshToken)) {
                    updateSecurityContext(userId)
                }
            }
        }
    }

    private fun updateSecurityContext(userId: Int) {
        SecurityContextHolder.getContext().authentication =
            UsernamePasswordAuthenticationToken(userId, null, Collections.emptyList())
    }

    private fun getTokenFromCookies(request: HttpServletRequest, tokenType: TokenType) =
        request.cookies?.find { cookie -> cookie.name == tokenType.value }

    private fun extendAccessToken(request: HttpServletRequest, response: HttpServletResponse, userId: Int) {
        getTokenFromCookies(request, REFRESH_TOKEN)?.let {
            if (tokenUtils.validateToken(it.value, REFRESH_TOKEN) == SUCCESS) {
                response.addCookie(TokenCookie.accessTokenCookie(tokenUtils.generateAccessToken(userId)).cookie)
            }
        }
    }
}

And last but not least, the ExpiredTokenHandler that was described above

@Service
class ExpiredTokenHandler(private val tokenRepository: RefreshTokenRepository, private val tokenUtils: JwtTokenUtils) {

    fun checkRefreshEligibility(response: HttpServletResponse, expiredToken: String, refreshToken: String?): Boolean {
        val userId = tokenUtils.getId(expiredToken)
        val savedRefreshToken = tokenRepository.findByUserId(userId)
        return if (validateRefreshToken(refreshToken, savedRefreshToken)) {
            val (newAccessToken, newRefreshToken) = generateNewTokens(userId, savedRefreshToken!!)
            updateCookies(newAccessToken, newRefreshToken, response)
            true
        } else {
            false
        }
    }

    private fun validateRefreshToken(
        refreshToken: String?,
        savedRefreshToken: RefreshToken?
    ): Boolean {
        return refreshToken != null &&
                savedRefreshToken != null &&
                refreshToken == savedRefreshToken.token &&
                tokenUtils.validateToken(refreshToken, REFRESH_TOKEN) == SUCCESS
    }

    private fun updateCookies(accessToken: String, refreshToken: String, response: HttpServletResponse): Boolean {
        response.addCookie(accessTokenCookie(accessToken).cookie)
        response.addCookie(refreshTokenCookie(refreshToken).cookie)
        return true
    }

    private fun generateNewTokens(userId: Int, savedRefreshToken: RefreshToken): Pair<String, String> {
        val newAccessToken = tokenUtils.generateAccessToken(userId)
        val newRefreshToken = tokenUtils.generateRefreshToken(userId)
        tokenRepository.save(savedRefreshToken.copy(token = newRefreshToken))
        return Pair(newAccessToken, newRefreshToken)
    }
}

That’s the core implementation, if you think that I missed something important then please let me know. I’m looking forward to hear what may be wrong with that approach. What’s more, I’m still Kotlin newbie, so perhaps I could utilize this language a bit more.


Get this bounty!!!

#StackBounty: #java #spring #spring-boot #hibernate Cannot create inner beanof type org.springframework.orm.jpa.SharedEntityManagerCrea…

Bounty: 50

I have deployed multitenant Spring boot application on AWS EC2. the code just works fine in the local system, but application is failing with below error after docker run in aws ec2.

] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderReportListener' defined in URL [jar:file:/app-service.jar!/BOOT-INF/classes!/com/aic/autofluence/appservice/scheduler/kafkaReportListener/OrderReportListener.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'reportFactoryImplData' defined in URL [jar:file:/app-service.jar!/BOOT-INF/classes!/com/aic/autofluence/appservice/scheduler/service/Impl/ReportFactoryImplData.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'orderReportRepository' defined in com.aic.autofluence.appservice.scheduler.repository.OrderReportRepository defined in @EnableJpaRepositories declared on TenantDatabaseConfig: Cannot create inner bean '(inner bean)#4293e066' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#4293e066': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
2021-05-10 18:58:57.081  INFO 1 --- [           main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'masterdb-persistence-unit'
2021-05-10 18:58:57.082  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : masterdb-connection-pool - Shutdown initiated...
2021-05-10 18:58:57.099  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : masterdb-connection-pool - Shutdown completed.
2021-05-10 18:58:57.103  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2021-05-10 18:58:57.124  INFO 1 --- [           main] ConditionEvaluationReportLoggingListener :

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-05-10 18:58:57.147 ERROR 1 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   :

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in com.aic.autofluence.appservice.scheduler.service.Impl.ReportFactoryImplData required a bean named 'entityManagerFactory' that could not be found.

Sample code I have as below:

tenantConfig: The EntityManagerFactory bean itself is not recognised. it is working good in local system, failing same in vm

@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = { "x.x.x.x.scheduler.repository", "x.x.x.x.scheduler.model" })
@EnableJpaRepositories(basePackages = {"x.x.x.x..scheduler.repository", "x.x.x.x..scheduler.service"},
        entityManagerFactoryRef = "tenantEntityManagerFactory",
        transactionManagerRef = "tenantTransactionManager")
public class TenantDatabaseConfig {

   @Bean(name = "tenantJpaVendorAdapter")
    public JpaVendorAdapter jpaVendorAdapter() {
        return new HibernateJpaVendorAdapter ();
    }

    @Bean(name = "tenantTransactionManager")
    public JpaTransactionManager transactionManager(@Qualifier("tenantEntityManagerFactory") EntityManagerFactory tenantEntityManager) {
        JpaTransactionManager transactionManager = new JpaTransactionManager ();
        transactionManager.setEntityManagerFactory(tenantEntityManager);
        return transactionManager;
    }
    
    @Bean(name = "datasourceBasedMultitenantConnectionProvider")
    @ConditionalOnBean(name = "masterEntityManagerFactory")
    public MultiTenantConnectionProvider multiTenantConnectionProvider() {
        return new DataSourceBasedMultiTenantConnectionProviderImpl();
    }

    @Bean(name = "currentTenantIdentifierResolver")
    public CurrentTenantIdentifierResolver currentTenantIdentifierResolver() {
        return new CurrentTenantIdentifierResolverImpl();
    }

    
    @Bean(name = "tenantEntityManagerFactory")
    @ConditionalOnBean(name = "datasourceBasedMultitenantConnectionProvider")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            @Qualifier("datasourceBasedMultitenantConnectionProvider")
                    MultiTenantConnectionProvider connectionProvider,
            @Qualifier("currentTenantIdentifierResolver")
                    CurrentTenantIdentifierResolver tenantResolver) {
        LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean ();
        //All tenant related entities, repositories and service classes must be scanned
        emfBean.setPackagesToScan("com.aic.autofluence.appservice");
        emfBean.setJpaVendorAdapter(jpaVendorAdapter());
        emfBean.setPersistenceUnitName("tenantdb-persistence-unit");
        Map<String, Object> properties = new HashMap<>();
        properties.put( Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
        properties.put( Environment.MULTI_TENANT_CONNECTION_PROVIDER, connectionProvider);
        properties.put( Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantResolver);
        properties.put( Environment.DIALECT, "org.hibernate.dialect.MySQL5Dialect");
        properties.put( Environment.SHOW_SQL, true);
        properties.put( Environment.FORMAT_SQL, true);
        properties.put( Environment.HBM2DDL_AUTO, "none");
        emfBean.setJpaPropertyMap(properties);
        return emfBean;
    }
}


**MasterConfig: It is configured properly working fine**

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = {"x.x.x.x.mastertenant.model",
        "x.x.x.x.mastertenant.repository"
},
        entityManagerFactoryRef = "masterEntityManagerFactory",
        transactionManagerRef = "masterTransactionManager")
public class MasterDatabaseConfig {

    private static final Logger LOG = LoggerFactory.getLogger(MasterDatabaseConfig.class);

    @Autowired
    private MasterDatabaseConfigProperties masterDbProperties;

    @Bean(name = "masterDataSource")
    public DataSource masterDataSource() {
        HikariDataSource hikariDataSource = new HikariDataSource ();
        hikariDataSource.setUsername(masterDbProperties.getUsername());
        hikariDataSource.setPassword(masterDbProperties.getPassword());
        hikariDataSource.setJdbcUrl(masterDbProperties.getUrl());
        hikariDataSource.setDriverClassName(masterDbProperties.getDriverClassName());
        hikariDataSource.setPoolName(masterDbProperties.getPoolName());
        // HikariCP settings
        hikariDataSource.setMaximumPoolSize(masterDbProperties.getMaxPoolSize());
        hikariDataSource.setMinimumIdle(masterDbProperties.getMinIdle());
        hikariDataSource.setConnectionTimeout(masterDbProperties.getConnectionTimeout());
        hikariDataSource.setIdleTimeout(masterDbProperties.getIdleTimeout());
        LOG.info("Setup of masterDataSource succeeded.");
        return hikariDataSource;
    }

    @Primary
    @Bean(name = "masterEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean masterEntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean ();
        em.setDataSource(masterDataSource());
        em.setPackagesToScan(new String[]{x,x,x,x...});
        em.setPersistenceUnitName("masterdb-persistence-unit");
        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter ();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(hibernateProperties());
        LOG.info("Setup of masterEntityManagerFactory succeeded.");
        return em;
    }

    @Bean(name = "masterTransactionManager")
    public JpaTransactionManager masterTransactionManager(@Qualifier("masterEntityManagerFactory") EntityManagerFactory emf) {
        JpaTransactionManager transactionManager = new JpaTransactionManager ();
        transactionManager.setEntityManagerFactory(emf);
        return transactionManager;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor ();
    }


    private Properties hibernateProperties() {
        Properties properties = new Properties();
        properties.put(org.hibernate.cfg.Environment.DIALECT, "org.hibernate.dialect.MySQL5Dialect");
        properties.put(org.hibernate.cfg.Environment.SHOW_SQL, true);
        properties.put(org.hibernate.cfg.Environment.FORMAT_SQL, true);
        properties.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, "none");
        return properties;
    }

Any Idea what might be the issue?

Thanks


Get this bounty!!!