Skip to content

Commit 274ba57

Browse files
committed
Improve getTimeout() endpoint
Use newer java.time.* API for generating serverTime and sessionExpiry. Use Spring ResponseCookie API for producing serverTime and sessionExpiry cookies. Add more tests for the generated cookies.
1 parent 45e5054 commit 274ba57

File tree

2 files changed

+50
-30
lines changed

2 files changed

+50
-30
lines changed

src/main/java/ubc/pavlab/rdp/controllers/MainController.java

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@
22

33
import lombok.extern.apachecommons.CommonsLog;
44
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.http.HttpHeaders;
56
import org.springframework.http.MediaType;
7+
import org.springframework.http.ResponseCookie;
8+
import org.springframework.http.ResponseEntity;
69
import org.springframework.security.access.prepost.PreAuthorize;
710
import org.springframework.stereotype.Controller;
811
import org.springframework.web.bind.annotation.GetMapping;
912
import org.springframework.web.bind.annotation.ResponseBody;
10-
import org.springframework.web.servlet.ModelAndView;
1113
import ubc.pavlab.rdp.services.UserService;
1214

13-
import javax.servlet.http.Cookie;
14-
import javax.servlet.http.HttpServletRequest;
15-
import javax.servlet.http.HttpServletResponse;
16-
import java.io.IOException;
15+
import javax.servlet.http.HttpSession;
16+
import java.time.Duration;
17+
import java.time.Instant;
1718

1819
@Controller
1920
@CommonsLog
@@ -29,16 +30,34 @@ public String index() {
2930
}
3031

3132
@GetMapping(value = { "/maintenance" })
32-
public ModelAndView maintenance() {
33-
return new ModelAndView( "error/maintenance" );
33+
public String maintenance() {
34+
return "error/maintenance";
3435
}
3536

3637
@PreAuthorize("isAuthenticated()")
3738
@GetMapping(value = "/gettimeout", produces = MediaType.TEXT_PLAIN_VALUE)
3839
@ResponseBody
39-
public String getTimeout( HttpServletRequest servletRequest, HttpServletResponse servletResponse ) {
40-
addTimeoutCookies( servletRequest, servletResponse );
41-
return "Session timeout refreshed.";
40+
public ResponseEntity<String> getTimeout( HttpSession httpSession ) {
41+
// Only set timeout cookie if the user is authenticated.
42+
Instant currTime = Instant.now();
43+
Duration timeoutInSeconds = Duration.ofSeconds( httpSession.getMaxInactiveInterval() ).minusSeconds( 60 ); // Subtracting by 60s to give an extra minute client-side.
44+
Instant expiryTime = currTime.plus( timeoutInSeconds );
45+
46+
// Get cookie for server current time.
47+
ResponseCookie serverTimeCookie = ResponseCookie.from( "serverTime", Long.toString( currTime.toEpochMilli() ) )
48+
.path( "/" )
49+
.build();
50+
51+
// Get cookie for expiration time (consistent with serverTime cookie).
52+
ResponseCookie sessionExpiryCookie = ResponseCookie.from( "sessionExpiry", Long.toString( expiryTime.toEpochMilli() ) )
53+
.path( "/" )
54+
.build();
55+
56+
return ResponseEntity.ok()
57+
.header( HttpHeaders.SET_COOKIE, serverTimeCookie.toString() )
58+
.header( HttpHeaders.SET_COOKIE, sessionExpiryCookie.toString() )
59+
.contentType( MediaType.TEXT_PLAIN )
60+
.body( "Session timeout refreshed." );
4261
}
4362

4463
@GetMapping(value = "/terms-of-service")
@@ -50,21 +69,4 @@ public String termsOfService() {
5069
public String privacyPolicy() {
5170
return "privacy-policy";
5271
}
53-
54-
private void addTimeoutCookies( HttpServletRequest servletRequest, HttpServletResponse servletResponse ) {
55-
// Only set timeout cookie if the user is authenticated.
56-
long currTime = System.currentTimeMillis();
57-
int TIMEOUT_IN_SECONDS = servletRequest.getSession().getMaxInactiveInterval() - 60; // Subtracting by 60s to give an extra minute client-side.
58-
long expiryTime = currTime + TIMEOUT_IN_SECONDS * 1000;
59-
60-
// Get cookie for server current time.
61-
Cookie serverTimeCookie = new Cookie( "serverTime", "" + currTime );
62-
serverTimeCookie.setPath( "/" );
63-
servletResponse.addCookie( serverTimeCookie );
64-
65-
// Get cookie for expiration time (consistent with serverTime cookie).
66-
Cookie expiryCookie = new Cookie( "sessionExpiry", "" + expiryTime );
67-
expiryCookie.setPath( "/" );
68-
servletResponse.addCookie( expiryCookie );
69-
}
7072
}

src/test/java/ubc/pavlab/rdp/controllers/MainControllerTest.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package ubc.pavlab.rdp.controllers;
22

3+
import org.hamcrest.FeatureMatcher;
4+
import org.hamcrest.Matcher;
35
import org.junit.Before;
46
import org.junit.Test;
57
import org.junit.runner.RunWith;
@@ -27,10 +29,10 @@
2729

2830
import java.util.EnumSet;
2931

32+
import static org.hamcrest.Matchers.closeTo;
3033
import static org.mockito.ArgumentMatchers.*;
3134
import static org.mockito.Mockito.when;
3235
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
33-
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
3436
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
3537
import static ubc.pavlab.rdp.util.TestUtils.createUser;
3638

@@ -141,12 +143,28 @@ public void getHtmlStats_redirect3xx() throws Exception {
141143
public void getTimeout_withUser_return200() throws Exception {
142144
User user = createUser( 1 );
143145
when( userService.findCurrentUser() ).thenReturn( user );
146+
long timeoutInSeconds = 0L;
144147
mvc.perform( get( "/gettimeout" ) )
145148
.andExpect( status().isOk() )
146149
.andExpect( content().contentTypeCompatibleWith( MediaType.TEXT_PLAIN ) )
147150
.andExpect( content().string( "Session timeout refreshed." ) )
148-
.andExpect( cookie().exists( "serverTime" ) )
149-
.andExpect( cookie().exists( "sessionExpiry" ) );
151+
.andExpect( cookie().value( "serverTime", asDouble( closeTo( System.currentTimeMillis(), 100 ) ) ) )
152+
.andExpect( cookie().path( "serverTime", "/" ) )
153+
.andExpect( cookie().secure( "serverTime", false ) )
154+
.andExpect( cookie().httpOnly( "serverTime", false ) )
155+
.andExpect( cookie().value( "sessionExpiry", asDouble( closeTo( System.currentTimeMillis() + 1000L * ( timeoutInSeconds - 60L ), 100 ) ) ) )
156+
.andExpect( cookie().path( "sessionExpiry", "/" ) )
157+
.andExpect( cookie().secure( "sessionExpiry", false ) )
158+
.andExpect( cookie().httpOnly( "sessionExpiry", false ) );
159+
}
160+
161+
private static FeatureMatcher<String, Double> asDouble( Matcher<Double> matcher ) {
162+
return new FeatureMatcher<String, Double>( matcher, "", "" ) {
163+
@Override
164+
protected Double featureValueOf( String s ) {
165+
return (double) Long.parseLong( s );
166+
}
167+
};
150168
}
151169

152170
@Test

0 commit comments

Comments
 (0)