I have a secure web service that I have been trying to test. The service looks something like this:
#GetMapping(value= {"/v1/getSomeStuff"})
public SomeResp getSomeStuff(final UsernamePasswordAuthenticationToken token, #RequestParam(name="searchString", required=true) String request) {
//do some stuff in here
}
The services are secured using Spring Security and I am trying to write an IT test, that will supply the required UsernamePasswordAuthenticationToken. I have tried doing this:
#Test
#Sql(scripts = { "/db-scripts/company-script.sql","/db-scripts/user-script.sql" })
#WithUserDetails(value="testuser")
public void testGetSomeStuff() {
//test some stuff
getRestTemplate().getForEntity("http://localhost:" + port + "/v1/getSomeStuff?searchString=foo", SomeResp.class);
}
The Sql scripts are executing correctly and the User Details Service is being called, but the token is not being passed to the service. I understand (now) that #WithUserDetails is for test method security and won't do what I want. My question then is, is there another method for testing secured web services using the various spring test classes?
Related
I'm currently working on a senior project and we decided to use Spring Webflux for our backend and Google as our OAuth2.0 provider. I am currently trying to run some integration tests using Spock and Groovy on some endpoints that are secured behind OAuth2.0 authentication. The endpoint does not use the Authentication principal for anything, is just not supposed to be accessed by someone who isn't authenticated. However, reading the Spring documentation and I came across the method for a webTestClient to use a mock open id connect login in which I might not need to do mock the entire OAuth2 process, however, this is giving me a HTTP 302 status
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, properties = "spring.main.web-application-type=reactive")
class UserControllerITSpec extends Specification {
#Autowired
ReactiveWebApplicationContext context
#Autowired
ApplicationContext applicationContext
#Autowired
WebTestClient client
#Rule
public WireMockRule wireMockRule = new WireMockRule(wireMockConfig()
.port(8077))
def setup() {
client = WebTestClient.bindToApplicationContext(context)
.configureClient()
.build()
}
def "getAllUsers is successful"() {
given: "a request"
def request = client.mutateWith(mockOidcLogin()).get().uri("/api/v2/users/getAllUsers")
stubFor(post("/graphql/")
.withHeader("Authorization", equalTo("F9v4MUqdQuWAh3Wqxe11mteqPfPedUqp78VaQNJt8DSt"))
.withHeader("content-type", equalTo("application/json"))
.withHeader("accept", equalTo("application/json"))
.withRequestBody(equalTo("""{
"query": "query { list_UserItems { _UserItems { _id email displayName appointments } } }",
"variables": null,
"operationName": null
}"""))
.willReturn(aResponse()
.withStatus(200)
.withBodyFile("vendiaResponses/getAllUsersResponse.json")))
stubFor(get("/oauth2/authorization/wiremock")
.willReturn(status(200)))
when: "the request is sent"
def response = request.exchange()
then: "an OK status is returned"
response.expectStatus()
.isOk()
}
}
Is my understanding of the mutateWith(mockOidcLogin()) method incorrect?
I'd consider using webEnvironment = SpringBootTest.WebEnvironment.MOCK
Are you sure about the Authentication implementation you have at runtime? (you can have Authentication auth auto-magically injected as #Controller method parameter and inspect it with your favorite debugger or log its class)
Isn't there anything of help in those samples: https://github.com/ch4mpy/spring-addons/tree/master/samples (look at those containing webflux in their name and focus on test classes ending with ItegrationTest in their name like this one)
If their isn't a test annotation for your spring Authentication type in the repo yet, open an issue. I might consider implement it.
When I get request form path for example /bar is it possible in spring cloud gateway to call multiple microservices and integrate their result (for example JSON) and send as response of /bar ?
How can i do it?
thanks
You can use ProxyExchange to help you composition multiple responses.
An example given by Spring Cloud:
#RestController
#SpringBootApplication
public class GatewaySampleApplication {
#Value("${remote.home}")
private URI home;
#GetMapping("/test")
public ResponseEntity<?> proxy(ProxyExchange<byte[]> proxy) throws Exception {
return proxy.uri(home.toString() + "/image/png").get();
}
}
In this case it is only used to return the ResponseEntity, but you can use it however you like. In your case you can combine multiple ResponseEntities.
Need a help with integration tests for Oauth2 client.
Setup:
Client with protected UI and API
Authentication server where all
password validation done and access token retrieved
Integration test:
rest-assured used for end-point testing
before implementing Oauth2 tests worked fine
Ole test example:
given().auth()
.preemptive()
.basic(USER_EMAIL,PASSWORD) <-- this not valid any more
.contentType(ContentType.JSON)
.when()
.pathParam("id","123")
.delete(PROFILE_FIELD_BASE_URL)
.andReturn()
.body();
Question:
how I can make this tests work again?
How res-assured setup should be changed to support oauth2?
Do I need to mock Authentication server or can I inject/mock security context?
The code you were shown is only and only for basic auth and for using the rest assured for OAuth, in general, you have to change that code. From REST Assured github page you can see following two exaples:
#Test public void
oauth2_works_with_preemptive_header_signing() {
final String accessToken = "accessToken";
given().
auth().preemptive().oauth2(accessToken).
filter(new Filter() {
public Response filter(FilterableRequestSpecification requestSpec, FilterableResponseSpecification responseSpec, FilterContext ctx) {
assertThat(requestSpec.getHeaders().getValue("Authorization"), equalTo("Bearer "+accessToken));
return new ResponseBuilder().setBody("ok").setStatusCode(200).build();
}
}).
when().
get("/somewhere").
then().
statusCode(200);
}
#Test public void
oauth2_works_with_non_preemptive_header_signing() {
final String accessToken = "accessToken";
given().
auth().oauth2(accessToken).
filter(new Filter() {
public Response filter(FilterableRequestSpecification requestSpec, FilterableResponseSpecification responseSpec, FilterContext ctx) {
AuthenticationScheme scheme = requestSpec.getAuthenticationScheme();
assertThat(scheme, instanceOf(PreemptiveOAuth2HeaderScheme.class));
assertThat(((PreemptiveOAuth2HeaderScheme) scheme).getAccessToken(), equalTo(accessToken));
return new ResponseBuilder().setBody("ok").setStatusCode(200).build();
}
}).
when().
get("/somewhere").
then().
statusCode(200);
}
and as the other example, you can have a look here.
First of all, i'm not a pro with tests, actually i'm learning to use them, and i'm finding myself in a lot of trouble trying to test Spring Controllers with the Spring Test framework, so i did the test after the production code, because i didn't know exactly how build the test.
The project is a Spring Boot (1.2.3) with Web, Thymeleaf, Security, MongoDB and Actuator modules, and using the platform-bom (1.1.2) for version management.
The functionality is easy, while the user fills a form, ajax requests using jQuery are sent to the server to verify the last field the user filled, then the server performs validation, and returns a JSON response.
Client side validation is not an option because some information has to be queried to a database to perform the validation (like if a username is already in use or not), so it needs to be done server side.
Also all the validation is performed with Hibernate Validator and custom annotations, both field and class annotations, since we use 1 entry point to perform the interactive form validation, the entire form is send in each request.
Here is the method signature (and first validations) in a #RestController :
#RequestMapping(value = "/some-url", method = RequestMethod.POST)
public ResponseEntity<ValidationResponse> validateFormField(
#Valid #ModelAttribute UserRegistrationForm form, BindingResult result) {
if (form == null || form.getSomeValue() == null) {
return new ResponseEntity<ValidationResponse>(HttpStatus.BAD_REQUEST);
}
// more input validation and actions
}
Now the call code, using jQuery:
$.ajax({
url: '/some-url',
type: 'post',
data: $(selector).serialize(),
dataType: 'json',
...
});
Now, the unit test code i'm writting to automatic test this is:
#ActiveProfiles("testing")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {
Application.class, WebConfiguration.class,
MongoConfiguration.class, SecurityConfiguration.class
})
#WebAppConfiguration
public class RegistrationInteractiveValidationRestControllerTest {
#Resource
private FilterChainProxy springSecurityFilterChain;
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockMvc = webAppContextSetup(wac)
.addFilter(springSecurityFilterChain)
.build();
}
#Test
public void withValidInput_returnStatusOK() throws Exception {
// test init
MockHttpServletRequestBuilder ajaxRequest = post("/some-url")
.content( "username=Bobby&email=& ...more fields... " )
.secure( true ) .with( csrf().asHeader() );
// test action
ResultActions performRequest = mockMvc.perform(ajaxRequest);
// test verification
performRequest.andExpect( status().isOk() );
}
#Test
public void withInvalidInput_returnStatusBadRequest() throws Exception {
// test init
MockHttpServletRequestBuilder ajaxRequest = post("/some-url")
.content( "{ 'im' : 'a bad content request' }" )
.secure( true ) .with( csrf().asHeader() );
// test action
ResultActions performRequest = mockMvc.perform(ajaxRequest);
// test verification
performRequest.andExpect(status().isBadRequest());
}
}
The second test goes alright, status code is 400 as expected, but in the first test, it fails because it also returns a 400.
I did some test debugging and the form object itself is not null, but all its fields are null.
But if i deploy the app in a server, and do the test manually, it works just fine, ajax calls get responses with valid JSON and HTTP 200 status codes.
I also did some debugging of the app deployed in a server and the databinding works just fine.
My thoughts are that i'm missing something building the request in the test, so what i've tried is to verify in the browser the request headers and form data when the app is deployed on a server, and added those to the requests i'm building in the tests, all of them, without any luck at all.
I've researched also the Spring Framework, Spring Boot, Spring security and Spring Test reference documentation, and I didn't find anything that could lead me to the right answer.
I've been looking for a way to authenticate a user via REST controller (URL params).
The closest thing to do so is the following:
#Controller
#RequestMapping(value="/api/user")
public class UserController extends BaseJSONController{
static Logger sLogger = Logger.getLogger(UserController.class);
#RequestMapping(value = "/login", method = RequestMethod.POST)
public #ResponseBody String login(#RequestParam(value="username") String user, #RequestParam(value="password") String pass) throws JSONException {
Authentication userAuth = new UsernamePasswordAuthenticationToken(user, pass);
MyCellebriteAuthenticationProvider MCAP = new MyCellebriteAuthenticationProvider();
if (MCAP.authenticate(userAuth) == null){
response.put("isOk", false);
}
else{
SecurityContextHolder.getContext().setAuthentication(userAuth);
response.put("isOk", true);
response.put("token", "1234");
}
return response.toString();
}
}
However, this doesn't create a cookie.
Any idea or a better way to implement what I want to achieve?
Firstly, you should not do this manually:
SecurityContextHolder.getContext().setAuthentication(userAuth)
It is better to employ special filter responsible for authentication, setting security context and clearing it after request is handled. By default Spring Security uses thread locals to store security context so if you don't remove it after client invocation, another client can be automatically logged in as someone else. Remember that server threads are often reused for different request by different clients.
Secondly, I would recommend using basic or digest authentication for your RESTful web service. Both are supported by Spring Security. More in docs http://static.springsource.org/spring-security/site/docs/3.1.x/reference/basic.html
And finally, remember that RESTful web service should be stateless.
Also remember that Spring Security documentation is your friend. :-)