TestCase error when generating microservice project with oauth2 authentication type
When running jhpack on new generated project,can't pass the test.
TestCase error when using ODIC.See below:
[ERROR] Failures:
[ERROR] AccountResourceIntTest.testGetExistingAccount:116 Status expected:<200> but was:<500>
[INFO]
[ERROR] Tests run: 95, Failures: 1, Errors: 0, Skipped: 0
.yo-rc.json file
{
"generator-jhipster": {
"promptValues": {
"packageName": "com.gzs.audit",
"nativeLanguage": "zh-cn"
},
"jhipsterVersion": "5.0.0-beta.0",
"baseName": "onlineshop",
"reactive": false,
"packageName": "com.gzs.audit",
"packageFolder": "com/gzs/audit",
"serverPort": "8081",
"authenticationType": "oauth2",
"cacheProvider": "hazelcast",
"enableHibernateCache": true,
"websocket": false,
"databaseType": "sql",
"devDatabaseType": "h2Disk",
"prodDatabaseType": "mysql",
"searchEngine": false,
"messageBroker": false,
"serviceDiscoveryType": "eureka",
"buildTool": "maven",
"enableSwaggerCodegen": true,
"jwtSecretKey": "replaced-by-jhipster-info",
"enableTranslation": true,
"applicationType": "microservice",
"testFrameworks": [],
"jhiPrefix": "jhi",
"nativeLanguage": "zh-cn",
"languages": [
"zh-cn"
],
"clientPackageManager": "yarn",
"skipClient": true,
"skipUserManagement": true
}
}
entityName.json files generated in the .jhipster directory
macos 10.13.4
[-] Checking this box is mandatory (this is just to show you read everything)
I confirm the issue.
Here the full log: https://travis-ci.org/hipster-labs/jhipster-travis-build/jobs/366427357
@tianlinzx +1
I'm looking into this now...
This seems to happen because the Principal is not populated when testing in a microservices environment.
public UserDTO getAccount(Principal principal) { ... }
This same code exists in a monolith and it works. My suspicion is there's something in the microservices security configuration that intercepts the Principal set in the test and nullifies it.
FWIW, if I change MicroserviceSecurityConfiguration from having @EnableResourceServer to having @EnableOAuth2Sso (like a monolith has), the test passes.
@jgrandja Any idea why the following test works fine when using @EnableOAuth2Sso, but not with @EnableResourceServer? For context, we have this test in both a monolith environment (that uses EnableOAuth2Sso) and a microservices environment (that uses EnableResourceServer).
You can see the configuration classes for each in the JHipster project on GitHub:
@Test
@Transactional
public void testGetExistingAccount() throws Exception {
Set<Authority> authorities = new HashSet<>();
Authority authority = new Authority();
authority.setName(AuthoritiesConstants.ADMIN);
authorities.add(authority);
User user = new User();
user.setId(RandomStringUtils.randomAlphanumeric(50));
user.setLogin("test");
user.setFirstName("john");
user.setLastName("doe");
user.setEmail("[email protected]");
user.setImageUrl("http://placehold.it/50x50");
user.setLangKey("en");
user.setAuthorities(authorities);
userRepository.save(user);
// create security-aware mockMvc
restUserMockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
restUserMockMvc.perform(get("/api/account")
.with(user(user.getLogin()).roles("ADMIN"))
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$.login").value("test"))
.andExpect(jsonPath("$.firstName").value("john"))
.andExpect(jsonPath("$.lastName").value("doe"))
.andExpect(jsonPath("$.email").value("[email protected]"))
.andExpect(jsonPath("$.imageUrl").value("http://placehold.it/50x50"))
.andExpect(jsonPath("$.langKey").value("en"))
.andExpect(jsonPath("$.authorities").value(AuthoritiesConstants.ADMIN));
}
The method this test hits is as follows:
/**
* GET /account : get the current user.
*
* @param principal the current user; resolves to null if not authenticated
* @return the current user
* @throws InternalServerErrorException 500 (Internal Server Error) if the user couldn't be returned
*/
@GetMapping("/account")
@Timed
@SuppressWarnings("unchecked")
public UserDTO getAccount(Principal principal) {
if (principal != null) {
if (principal instanceof OAuth2Authentication) {
return userService.getUserFromAuthentication((OAuth2Authentication) principal);
} else {
// Allow Spring Security Test to be used to mock users in the database
return userService.getUserWithAuthorities()
.map(UserDTO::new)
.orElseThrow(() -> new InternalServerErrorException("User could not be found"));
}
} else {
throw new InternalServerErrorException("User could not be found");
}
}
In a microservices environment, the Principal is null. Thanks in advance for any advice!
Edit: You can reproduce this issue using the following commands:
git clone https://github.com/mraible/jhipster5-ms-user.git
cd jhipster5-ms-user/blog
./mvnw test
I'm not sure AccountResource and AccountResourceIntTest should exist when applicationType === 'microservice' because account.service.ts only exists on the gateway. I'll make a PR to remove it.
@mraible If you could put together a minimal sample the reproduces the issue that would be more efficient on my end. I will say that @EnableOAuth2Sso can be tricky at times as it applies a number of conditional checks on properties rooted at security.oauth2.resource.*.
Are you able to use @EnableResourceServer exclusively?
@jgrandja It's probably something in JHipster's configuration that causes this, so I'm not sure a minimal sample would help. I think these steps are pretty simple though! ;)
git clone https://github.com/mraible/jhipster5-ms-user.git
cd jhipster5-ms-user/blog
./mvnw test -Dtest=AccountResourceIntTest
The test will fail:
[ERROR] Failures:
[ERROR] AccountResourceIntTest.testGetExistingAccount:127 Status expected:<200> but was:<500>
Change from @EnableResourceServer to @EnableOAuth2Sso in blog/src/main/java/com/okta/developer/blog/config/MicroserviceSecurityConfiguration.java and it passes.
@mraible The reason @EnableResourceServer results in a null Principal in getAccount(Principal principal) is because of the configured RequestMatcher in MicroserviceSecurityConfiguration:
.requestMatcher(authorizationHeaderRequestMatcher())
.authorizeRequests()
.antMatchers("/api/profile-info").permitAll()
.antMatchers("/api/**").authenticated()
The RequestHeaderRequestMatcher("Authorization") is configured but the request in the test does not set the Authorization header which results in no matching FilterChain. So the request passes through without going through any of the Spring Security filters and therefore the Authentication doesn't get resolved.
However, even if you comment out .requestMatcher(authorizationHeaderRequestMatcher()) which will match on a FilterChain (AnyRequestMatcher) a 401 error will occur. So other things will need to be changed in that test to make it pass.
@jgrandja I suspected that might be the case. There's nothing in spring-security-test that allows you to auto-send a Bearer token in an Authorization header is there? I was hoping @WithMockUser or .with(user(user.getLogin()).roles("ADMIN")) could do that somehow.
There's nothing in spring-security-test that allows you to auto-send a Bearer token in an Authorization header is there?
No there isn't. However, we plan on adding oauth test support in Spring Security 5.x
Most helpful comment
No there isn't. However, we plan on adding oauth test support in Spring Security 5.x