Merge pull request 'refresh-token' (#35) from refresh-token into master

Accepted
Reviewed-on: http://git.plannaplan.pl/filipizydorczyk/backend/pulls/35
This commit is contained in:
Marcin Woźniak 2020-12-23 12:41:03 +01:00
commit 76cf5c8e96
8 changed files with 97 additions and 11 deletions

View File

@ -1,6 +1,7 @@
package com.plannaplan.entities; package com.plannaplan.entities;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.persistence.Entity; import javax.persistence.Entity;
@ -28,6 +29,7 @@ public class User {
private String usosId; private String usosId;
private UserRoles role; private UserRoles role;
private String token; private String token;
private String refreshToken;
private Timestamp tokenUsageDate; private Timestamp tokenUsageDate;
public User() { public User() {
@ -105,6 +107,13 @@ public class User {
return token; return token;
} }
/**
* @return token needed to call refresh token after it expired
*/
public String getRefreshToken() {
return refreshToken;
}
/** /**
* token seter. Sets token and automaticly set time when was set * token seter. Sets token and automaticly set time when was set
* *
@ -113,6 +122,7 @@ public class User {
public void setToken(String token) { public void setToken(String token) {
this.tokenUsageDate = new Timestamp(System.currentTimeMillis()); this.tokenUsageDate = new Timestamp(System.currentTimeMillis());
this.token = token; this.token = token;
this.refreshToken = UUID.randomUUID().toString();
} }
/** /**

View File

@ -38,6 +38,9 @@ public interface UserRepository extends JpaRepository<User, Long> {
@Query("FROM User WHERE email = ?1") @Query("FROM User WHERE email = ?1")
Optional<User> getByAuthority(@Param("authority") String authority); Optional<User> getByAuthority(@Param("authority") String authority);
@Query("FROM User WHERE refreshToken = ?1")
Optional<User> getByRefreshToken(@Param("refreshToken") String refreshToken);
@Query("FROM User WHERE token = ?1") @Query("FROM User WHERE token = ?1")
Optional<User> getByToken(@Param("token") String token); Optional<User> getByToken(@Param("token") String token);

View File

@ -1,22 +1,17 @@
package com.plannaplan.services; package com.plannaplan.services;
import org.springframework.beans.factory.annotation.Autowired; // import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
public class EventService { public class EventService {
@Autowired // @Autowired
private EmailService emailService; // private EmailService emailService;
@Scheduled(cron = "0 2 17 * * *") @Scheduled(cron = "0 2 17 * * *")
public void collectGroupLosses() { public void collectGroupLosses() {
System.out.println("Checking for groups"); System.out.println("Checking for groups");
} }
@Scheduled(cron = "0 5 18 * * *")
public void testMail() {
this.emailService.sendMail("kaczor982@gmail.com", "Cześć. Jestem Tomek.");
}
} }

View File

@ -81,4 +81,8 @@ public class UserService {
return this.repo.getAllByRole(UserRoles.STUDENT); return this.repo.getAllByRole(UserRoles.STUDENT);
} }
public Optional<User> getUserByRefreshToken(String refreshToken) {
return this.repo.getByRefreshToken(refreshToken);
}
} }

View File

@ -1,5 +1,8 @@
package com.plannaplan.controllers; package com.plannaplan.controllers;
import java.util.Optional;
import java.util.UUID;
import com.plannaplan.entities.User; import com.plannaplan.entities.User;
import com.plannaplan.exceptions.UserNotFoundException; import com.plannaplan.exceptions.UserNotFoundException;
import com.plannaplan.responses.models.TokenResponse; import com.plannaplan.responses.models.TokenResponse;
@ -61,4 +64,20 @@ public class TokenController {
} }
} }
@GetMapping("/token/refresh")
@ApiOperation(value = "Endpoint to access new token based on refresh token. It's needed when request with provided token fail with code 403")
public ResponseEntity<TokenResponse> getRefreshToken(
@RequestParam("refreshToken") @ApiParam(value = "Refresh token obtained in /token request") final String refreshToken)
throws NullPointerException {
Optional<User> userResult = this.userService.getUserByRefreshToken(refreshToken);
if (userResult.isEmpty()) {
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
User user = userResult.get();
user.setToken(UUID.randomUUID().toString());
user = this.userService.save(user);
return new ResponseEntity<>(new TokenResponse(user), HttpStatus.OK);
}
} }

View File

@ -9,6 +9,8 @@ import io.swagger.annotations.ApiModelProperty;
public class TokenResponse { public class TokenResponse {
@ApiModelProperty(value = "user token used to verify requests") @ApiModelProperty(value = "user token used to verify requests")
private String token; private String token;
@ApiModelProperty(value = "user token needed to refresh")
private String refreshToken;
@ApiModelProperty(value = "user id in database") @ApiModelProperty(value = "user id in database")
private Long id; private Long id;
@ApiModelProperty(value = "user app role") @ApiModelProperty(value = "user app role")
@ -21,6 +23,7 @@ public class TokenResponse {
this.authorityRole = user.getRole().toString(); this.authorityRole = user.getRole().toString();
this.email = user.getEmail(); this.email = user.getEmail();
this.token = user.getToken(); this.token = user.getToken();
this.refreshToken = user.getRefreshToken();
} }
public String getEmail() { public String getEmail() {
@ -39,4 +42,8 @@ public class TokenResponse {
return token; return token;
} }
public String getRefreshToken() {
return this.refreshToken;
}
} }

View File

@ -37,9 +37,10 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override @Override
public void configure(final WebSecurity webSecurity) { public void configure(final WebSecurity webSecurity) {
webSecurity.ignoring().antMatchers("/token**").antMatchers("/api/v1/courses/all") webSecurity.ignoring().antMatchers("/token**").antMatchers("/token/refresh**")
.antMatchers("/api/v1/groups/course/{id}").antMatchers("/v2/api-docs", "/configuration/ui", .antMatchers("/api/v1/courses/all").antMatchers("/api/v1/groups/course/{id}")
"/swagger-resources/**", "/configuration/security", "/swagger-ui.html", "/webjars/**"); .antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources/**", "/configuration/security",
"/swagger-ui.html", "/webjars/**");
} }
@Override @Override

View File

@ -3,6 +3,7 @@ package com.plannaplan.controllers;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
@ -12,11 +13,22 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import com.plannaplan.entities.User;
import com.plannaplan.services.UserService;
import com.plannaplan.types.UserRoles;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest
@ContextConfiguration @ContextConfiguration
public class TokenControllerTest extends AbstractControllerTest { public class TokenControllerTest extends AbstractControllerTest {
private final String TOKEN_ENDPOINT = "/token"; private final String TOKEN_ENDPOINT = "/token";
private final String REFRESH_TOKEN_ENDPOINT = "/token/refresh";
@Autowired
private UserService service;
@Test @Test
public void shouldFailWithNoParameter() throws Exception { public void shouldFailWithNoParameter() throws Exception {
@ -39,4 +51,39 @@ public class TokenControllerTest extends AbstractControllerTest {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
mockMvc.perform(get(TOKEN_ENDPOINT).param("ticket", ticket)).andExpect(status().isOk()); mockMvc.perform(get(TOKEN_ENDPOINT).param("ticket", ticket)).andExpect(status().isOk());
} }
@Test
public void shouldFailWithWrongRefreshToken() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
mockMvc.perform(get(REFRESH_TOKEN_ENDPOINT).param("refreshToken", "totaly-wrong-refresh-token"))
.andExpect(status().is4xxClientError());
}
@Test
public void shouldRetrunTokenBeforeExpirationOfOldOne() throws Exception {
User user = new User("Eren", "Yager", "shouldRetrunTokenBeforeExpirationOfOldOne@TokenController.test",
UserRoles.TEST_USER);
user.setToken("Totalnie-bezpieczny-token");
this.service.save(user);
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
mockMvc.perform(get(REFRESH_TOKEN_ENDPOINT).param("refreshToken", user.getRefreshToken()))
.andExpect(status().isOk());
}
@Test
public void shouldRetrunTokenAfterExpirationOfOldOne() throws Exception {
final Field reader = User.class.getDeclaredField("tokenUsageDate");
reader.setAccessible(true);
User user = new User("Mikasa", "Ackerman", "shouldRetrunTokenAfterExpirationOfOldOne@TokenController.test",
UserRoles.TEST_USER);
user.setToken("Totalnie-bezpieczny-token");
reader.set(user, new Timestamp(System.currentTimeMillis() - 86400000));
this.service.save(user);
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
mockMvc.perform(get(REFRESH_TOKEN_ENDPOINT).param("refreshToken", user.getRefreshToken()))
.andExpect(status().isOk());
}
} }