From 0152f47d54540a9b5a420c8041f76ae1e2fadc91 Mon Sep 17 00:00:00 2001 From: Fifolu Akinyemi Date: Tue, 29 Aug 2023 07:42:57 +0100 Subject: [PATCH] Complete access and refresh token implementation. --- pom.xml | 31 ++++---- .../HolidayPlannerApplication.java | 3 +- .../AvailableDatesController.java | 8 +- .../budget/BudgetController.java | 8 +- .../config/SecurityConfigurer.java | 34 ++++---- .../holidayplanner/config/SwaggerConfig.java | 78 +++++-------------- .../config/jwt/JwtRequestFilter.java | 22 +++--- .../holidayplanner/config/jwt/JwtUtil.java | 28 ++++--- .../config/jwt/token/Token.java | 13 +++- .../config/jwt/token/TokenRepository.java | 3 +- .../config/jwt/token/TokenService.java | 5 +- .../holidayplanner/group/GroupController.java | 18 ++--- .../groupInvite/GroupInviteController.java | 10 +-- .../holiday/HolidayController.java | 14 ++-- .../HolidayInviteController.java | 8 +- .../HolidayPreferencesController.java | 4 +- .../holidayplanner/user/UserController.java | 36 ++++----- .../holidayplanner/user/UserService.java | 8 +- src/main/resources/application.properties | 3 +- 19 files changed, 158 insertions(+), 176 deletions(-) diff --git a/pom.xml b/pom.xml index ea04cde..2f65b9c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.1.3 + 3.0.1 com.example @@ -14,7 +14,7 @@ holiday-planner Holiday Planner - 16 + 17 @@ -26,6 +26,7 @@ org.springframework.boot spring-boot-starter-web + 3.1.3 @@ -52,21 +53,23 @@ - io.springfox - springfox-swagger2 - 3.0.0 + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.0.2 + - io.springfox - springfox-swagger-ui - 3.0.0 + org.springdoc + springdoc-openapi-security + 1.7.0 - io.springfox - springfox-boot-starter - 3.0.0 + javax.servlet + javax.servlet-api + 4.0.1 + provided @@ -129,12 +132,6 @@ runtime - - org.springframework.data - spring-data-mongodb - 3.3.0 - - org.mongodb diff --git a/src/main/java/com/example/holidayplanner/HolidayPlannerApplication.java b/src/main/java/com/example/holidayplanner/HolidayPlannerApplication.java index 3317d36..2d502b9 100644 --- a/src/main/java/com/example/holidayplanner/HolidayPlannerApplication.java +++ b/src/main/java/com/example/holidayplanner/HolidayPlannerApplication.java @@ -1,5 +1,6 @@ package com.example.holidayplanner; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; @@ -7,7 +8,7 @@ @SpringBootApplication() @EnableScheduling -@EnableMongoRepositories +@OpenAPIDefinition public class HolidayPlannerApplication { public static void main(String[] args) { diff --git a/src/main/java/com/example/holidayplanner/availableDates/AvailableDatesController.java b/src/main/java/com/example/holidayplanner/availableDates/AvailableDatesController.java index 59babf7..ad9d8cf 100644 --- a/src/main/java/com/example/holidayplanner/availableDates/AvailableDatesController.java +++ b/src/main/java/com/example/holidayplanner/availableDates/AvailableDatesController.java @@ -1,9 +1,9 @@ package com.example.holidayplanner.availableDates; import com.fasterxml.jackson.core.JsonProcessingException; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -15,7 +15,7 @@ @RestController @RequestMapping("/api/v1.0/availabledates") -@Api(tags = "Available Dates") +@Tag(name = "Available Dates") @SecurityRequirement(name = "holidayPlannerSecurity") public class AvailableDatesController { @@ -28,7 +28,7 @@ public AvailableDatesController(AvailableDatesService availableDatesService) { @PostMapping(path = "/findmultiplebyid") - @ApiOperation(value = "Find multiple available dates by id") + @Operation(summary = "Find multiple available dates by id") public ResponseEntity findMultipleById(@RequestBody List availableDatesIds) throws JsonProcessingException { return availableDatesService.findMultipleById(availableDatesIds); } diff --git a/src/main/java/com/example/holidayplanner/budget/BudgetController.java b/src/main/java/com/example/holidayplanner/budget/BudgetController.java index 445ed9f..5f05eba 100644 --- a/src/main/java/com/example/holidayplanner/budget/BudgetController.java +++ b/src/main/java/com/example/holidayplanner/budget/BudgetController.java @@ -1,9 +1,9 @@ package com.example.holidayplanner.budget; import com.fasterxml.jackson.core.JsonProcessingException; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -15,7 +15,7 @@ @RestController @RequestMapping("/api/v1.0/budgets") -@Api(tags = "Budgets") +@Tag(name = "Budgets") @SecurityRequirement(name = "holidayPlannerSecurity") public class BudgetController { @@ -27,7 +27,7 @@ public BudgetController(BudgetService budgetService) { } @PostMapping(path = "/findmultiplebyid") - @ApiOperation(value = "Find multiple budgets by id") + @Operation(summary = "Find multiple budgets by id") public ResponseEntity findMultipleById(@RequestBody List budgetIds) throws JsonProcessingException { return budgetService.findMultipleById(budgetIds); } diff --git a/src/main/java/com/example/holidayplanner/config/SecurityConfigurer.java b/src/main/java/com/example/holidayplanner/config/SecurityConfigurer.java index 9044d19..a45ed72 100644 --- a/src/main/java/com/example/holidayplanner/config/SecurityConfigurer.java +++ b/src/main/java/com/example/holidayplanner/config/SecurityConfigurer.java @@ -1,7 +1,7 @@ package com.example.holidayplanner.config; import com.example.holidayplanner.config.jwt.JwtRequestFilter; -import org.apache.catalina.filters.CorsFilter; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -15,6 +15,9 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.session.SessionManagementFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; import static org.springframework.security.config.Customizer.withDefaults; @@ -27,35 +30,21 @@ public class SecurityConfigurer { @Autowired private JwtRequestFilter jwtRequestFilter; -// @Override -// protected void configure(AuthenticationManagerBuilder auth) throws Exception { -// auth.userDetailsService(myUserDetailsService); -// } - @Bean public SecurityFilterChain configure (HttpSecurity http) throws Exception { return http.cors(withDefaults()) .csrf((csrf) -> csrf.disable()) .authorizeHttpRequests((authorize) -> authorize - .requestMatchers("/api/v1.0/users/newuser", "/api/v1.0/users/login", "/", "/csrf", "/v3/api-docs", + .requestMatchers("/api/v1.0/users/newuser", "/api/v1.0/users/login", "/", "/csrf", "/swagger-resources/configuration/ui", "/configuration/ui", - "/swagger-resources", "/swagger-resources/configuration/security", - "/configuration/security", "/swagger-ui/**", "/v2/api-docs/**", "/webjars/**", "/swagger-ui.html").permitAll()) + "/swagger-resources/**", "/swagger-resources/configuration/security", + "/configuration/security", "configuration/**", "/swagger-ui/**", "/v3/api-docs/**", "/webjars/**", "/swagger-ui.html").permitAll() + .anyRequest().authenticated()) .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .addFilterBefore( jwtRequestFilter, UsernamePasswordAuthenticationFilter.class ) .addFilterBefore(corsFilter(), SessionManagementFilter.class).build(); } -// @Override -// public void configure(WebSecurity web) throws Exception { -// web.ignoring().antMatchers("/api/v1.0/users/newuser"); -// web.ignoring().antMatchers("/api/v1.0/users/login"); -// web.ignoring().antMatchers("/", "/csrf", "/v3/api-docs", -// "/swagger-resources/configuration/ui", "/configuration/ui", -// "/swagger-resources", "/swagger-resources/configuration/security", -// "/configuration/security", "/swagger-ui/**", "/v2/api-docs/**", "/webjars/**", "/swagger-ui.html"); -// } - @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); @@ -68,7 +57,12 @@ public AuthenticationManager authenticationManagerBean(final AuthenticationConfi @Bean CorsFilter corsFilter() { - CorsFilter corsFilter = new CorsFilter(); + CorsFilter corsFilter = new CorsFilter(new CorsConfigurationSource() { + @Override + public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { + return null; + } + }); return corsFilter; } } diff --git a/src/main/java/com/example/holidayplanner/config/SwaggerConfig.java b/src/main/java/com/example/holidayplanner/config/SwaggerConfig.java index 88996ad..85f7bb9 100644 --- a/src/main/java/com/example/holidayplanner/config/SwaggerConfig.java +++ b/src/main/java/com/example/holidayplanner/config/SwaggerConfig.java @@ -1,71 +1,31 @@ package com.example.holidayplanner.config; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import springfox.documentation.builders.ApiInfoBuilder; -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.service.*; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spi.service.contexts.SecurityContext; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; - -import java.time.LocalDate; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; @Configuration -@EnableSwagger2 public class SwaggerConfig implements WebMvcConfigurer { @Bean - public Docket api() { - return new Docket(DocumentationType.SWAGGER_2) - .apiInfo(apiInfo()) - .securityContexts(Arrays.asList(securityContext())) - .securitySchemes(Arrays.asList(apiKey())) - .pathMapping("/") - .select() - .apis(RequestHandlerSelectors.any()) - .paths(PathSelectors.any()) - .build() - .tags(new Tag("User", "All User Endpoints"), new Tag("Group", "All Group Endpoints"), new Tag("Holiday", "All Holiday Endpoints")) - - .directModelSubstitute(LocalDate.class, String.class) - .genericModelSubstitutes(ResponseEntity.class) - ; - } - - ApiInfo apiInfo() { - return new ApiInfoBuilder() - .title("Holiday Planner") - .version("1.0.0") - .description("Swagger page for the Holiday Planner Monolith") - .build() - ; - } - - @Override - public void addViewControllers(final ViewControllerRegistry registry) { - registry.addRedirectViewController("/", "/swagger-ui/#/"); - } - - private ApiKey apiKey () { - return new ApiKey("Access Token", "Authorization", SecurityScheme.In.HEADER.name()); - } - - private SecurityContext securityContext() { - return SecurityContext.builder().securityReferences(securityReference()).build(); - } - - private List securityReference() { - AuthorizationScope[] authorizationScope = { new AuthorizationScope("global", "Empty Description")}; - return Collections.singletonList(new SecurityReference("Access Token", authorizationScope)); + public OpenAPI springShopOpenAPI() { + return new OpenAPI().info(new Info().title("GetAway") + .description("Api documentation for GetAway") + .version("v0.01") + .license(new License() + .name("Apache 2.0").url("http://springdoc.org") + )) + .components(new Components().addSecuritySchemes("Api Key", new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .scheme("bearer") + .bearerFormat("jwt") + .in(SecurityScheme.In.HEADER) + .name("Authorization") + )).addSecurityItem(new SecurityRequirement().addList("Api Key")); } } diff --git a/src/main/java/com/example/holidayplanner/config/jwt/JwtRequestFilter.java b/src/main/java/com/example/holidayplanner/config/jwt/JwtRequestFilter.java index ac2bf74..e50bfa9 100644 --- a/src/main/java/com/example/holidayplanner/config/jwt/JwtRequestFilter.java +++ b/src/main/java/com/example/holidayplanner/config/jwt/JwtRequestFilter.java @@ -16,6 +16,7 @@ import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; +import java.util.Date; @Component public class JwtRequestFilter extends OncePerRequestFilter { @@ -23,12 +24,13 @@ public class JwtRequestFilter extends OncePerRequestFilter { private MyUserDetailsService myUserDetailsService; @Autowired - private JwtUtil jwtUtil; + private final JwtUtil jwtUtil; @Autowired private final TokenService tokenService; - public JwtRequestFilter(TokenService tokenService) { + public JwtRequestFilter(JwtUtil jwtUtil, TokenService tokenService) { + this.jwtUtil = jwtUtil; this.tokenService = tokenService; } @@ -38,31 +40,33 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse final String authorizationHeader = request.getHeader("Authorization"); String jwt = null; - String email = null; +// String email = null; if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { jwt = authorizationHeader.substring(7); - email = jwtUtil.extractEmail(jwt); +// email = jwtUtil.extractEmail(jwt); } - if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) { - UserDetails userDetails = this.myUserDetailsService.loadUserByUsername(email); - + if (jwt != null && SecurityContextHolder.getContext().getAuthentication() == null) { Token token = tokenService.findByAccessToken(jwt); - if (!jwtUtil.validateAccessToken(token.getAccessToken(), userDetails) && jwtUtil.validateRefreshToken(token.getRefreshToken(), userDetails)) { + UserDetails userDetails = this.myUserDetailsService.loadUserByUsername(token.getOwner().getEmail()); + + if (!jwtUtil.validateAccessToken(token.getAccessToken()) && token.getRefreshTokenExpiration().after(new Date())) { String newAccessToken = jwtUtil.generateToken(userDetails); String newRefreshToken = jwtUtil.generateRefreshToken(userDetails); token.setAccessToken(newAccessToken); + token.setAccessTokenExpiration(jwtUtil.extractExpiration(newAccessToken)); token.setRefreshToken(newRefreshToken); + token.setRefreshTokenExpiration(jwtUtil.extractExpiration(newRefreshToken)); tokenService.saveToken(token); response.setHeader("NewAccessToken", newAccessToken); } - if (jwtUtil.validateAccessToken(token.getAccessToken(), userDetails)) { + if (jwtUtil.validateAccessToken(token.getAccessToken())) { UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); diff --git a/src/main/java/com/example/holidayplanner/config/jwt/JwtUtil.java b/src/main/java/com/example/holidayplanner/config/jwt/JwtUtil.java index eaae0b6..a076a58 100644 --- a/src/main/java/com/example/holidayplanner/config/jwt/JwtUtil.java +++ b/src/main/java/com/example/holidayplanner/config/jwt/JwtUtil.java @@ -1,9 +1,8 @@ package com.example.holidayplanner.config.jwt; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SignatureException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; @@ -37,14 +36,12 @@ public Date extractExpiration(String token) { return extractClaim(token, Claims::getExpiration); } - public Boolean validateAccessToken(String token, UserDetails userDetails) { - final String userName = extractEmail(token); - return (userName.equals(userDetails.getUsername()) && !isTokenExpired(token)); + public Boolean validateAccessToken(String accessToken) { + return (isTokenValid(accessToken) && !isTokenExpired(accessToken)); } - public Boolean validateRefreshToken(String token, UserDetails userDetails) { - final String userName = extractEmail(token); - return (userName.equals(userDetails.getUsername()) && !isTokenExpired(token)); + public Boolean validateRefreshToken(String refeshToken) { + return (isTokenValid(refeshToken) && !isTokenExpired(refeshToken)); } public String generateRefreshToken(UserDetails userDetails) { @@ -68,7 +65,7 @@ private String createToken(Map claims, UserDetails userDetails) return Jwts.builder().setClaims(claims) .setSubject(userDetails.getUsername()) .setIssuedAt( new Date(System.currentTimeMillis()) ) - .setExpiration( new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(12)) ) + .setExpiration( new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(60)) ) .signWith(secretKey).compact(); } @@ -76,5 +73,16 @@ private Boolean isTokenExpired(String token) { return extractExpiration(token).before(new Date()); } + private Boolean isTokenValid(String token) { + try { + Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(token); + return true; + } catch (ExpiredJwtException | MalformedJwtException | SignatureException exception) { + return false; + } + } } diff --git a/src/main/java/com/example/holidayplanner/config/jwt/token/Token.java b/src/main/java/com/example/holidayplanner/config/jwt/token/Token.java index 8ba260f..8f25570 100644 --- a/src/main/java/com/example/holidayplanner/config/jwt/token/Token.java +++ b/src/main/java/com/example/holidayplanner/config/jwt/token/Token.java @@ -1,10 +1,15 @@ package com.example.holidayplanner.config.jwt.token; +import com.example.holidayplanner.config.cascadeSaveMongoEventListener.CascadeSave; +import com.example.holidayplanner.user.User; import lombok.Data; +import org.springframework.data.mongodb.core.mapping.DBRef; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.FieldType; import org.springframework.data.mongodb.core.mapping.MongoId; +import java.util.Date; + @Document(collection = "Tokens") @Data public class Token { @@ -12,9 +17,15 @@ public class Token { @MongoId(value = FieldType.OBJECT_ID) private String id; - private String userId; + @DBRef + @CascadeSave + private User owner; private String accessToken; private String refreshToken; + + private Date accessTokenExpiration; + + private Date refreshTokenExpiration; } diff --git a/src/main/java/com/example/holidayplanner/config/jwt/token/TokenRepository.java b/src/main/java/com/example/holidayplanner/config/jwt/token/TokenRepository.java index e5060ec..04365d1 100644 --- a/src/main/java/com/example/holidayplanner/config/jwt/token/TokenRepository.java +++ b/src/main/java/com/example/holidayplanner/config/jwt/token/TokenRepository.java @@ -1,9 +1,10 @@ package com.example.holidayplanner.config.jwt.token; +import com.example.holidayplanner.user.User; import org.springframework.data.mongodb.repository.MongoRepository; public interface TokenRepository extends MongoRepository { - Token findByUserId(String userId); + Token findByOwner(User user); Token findByAccessToken(String accessToken); diff --git a/src/main/java/com/example/holidayplanner/config/jwt/token/TokenService.java b/src/main/java/com/example/holidayplanner/config/jwt/token/TokenService.java index 30b7dc9..72413fd 100644 --- a/src/main/java/com/example/holidayplanner/config/jwt/token/TokenService.java +++ b/src/main/java/com/example/holidayplanner/config/jwt/token/TokenService.java @@ -1,5 +1,6 @@ package com.example.holidayplanner.config.jwt.token; +import com.example.holidayplanner.user.User; import org.springframework.stereotype.Service; @Service public class TokenService { @@ -13,8 +14,8 @@ public Token saveToken(Token token) { return tokenRepository.save(token); } - public Token findByUserId(String userId) { - return tokenRepository.findByUserId(userId); + public Token findByUserId(User user) { + return tokenRepository.findByOwner(user); } public Token findByAccessToken(String accessToken) { diff --git a/src/main/java/com/example/holidayplanner/group/GroupController.java b/src/main/java/com/example/holidayplanner/group/GroupController.java index 735ad10..2b828eb 100644 --- a/src/main/java/com/example/holidayplanner/group/GroupController.java +++ b/src/main/java/com/example/holidayplanner/group/GroupController.java @@ -2,9 +2,9 @@ import com.example.holidayplanner.interfaces.ControllerInterface; import com.fasterxml.jackson.core.JsonProcessingException; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.validation.Errors; @@ -15,7 +15,7 @@ @RestController @RequestMapping(path = "/api/v1.0/groups") -@Api(tags = "Group") +@Tag(name = "Group") @SecurityRequirement(name = "holidayPlannerSecurity") public class GroupController implements ControllerInterface { @@ -57,37 +57,37 @@ public String removeGroupMember(@PathVariable("groupId") String groupId, @Reques @PostMapping(path = "/findmultiplebyid") - @ApiOperation(value = "Find multiple groups by their ids") + @Operation(summary = "Find multiple groups by their ids") public ResponseEntity findMultipleById(@RequestBody List groupIds) throws JsonProcessingException { return groupService.findMultipleById(groupIds); } @GetMapping(path = "/findbyid/{groupId}") - @ApiOperation(value = "Find a group by its id") + @Operation(summary = "Find a group by its id") public ResponseEntity findById(@PathVariable("groupId") String groupId) throws JsonProcessingException { return groupService.findById(groupId); } @GetMapping(path = "/search/{searchTerm}") - @ApiOperation(value = "Search for a group") + @Operation(summary = "Search for a group") public ResponseEntity> search(@PathVariable("searchTerm") String searchTerm) throws JsonProcessingException { return groupService.search(searchTerm); } @PostMapping(path = "/invite/{groupId}/{inviteeId}") - @ApiOperation(value = "Invite multiple users to a group") + @Operation(summary = "Invite multiple users to a group") public ResponseEntity inviteUsers(@PathVariable("groupId") String groupId, @PathVariable("inviteeId") String inviteeId, @RequestBody List userIds) throws JsonProcessingException { return groupService.inviteUsers(groupId, inviteeId, userIds); } @PostMapping(path = "/acceptinvite/{groupInviteId}/{userId}") - @ApiOperation(value = "Accept an invitation to a group") + @Operation(summary = "Accept an invitation to a group") public ResponseEntity acceptInvitation(@PathVariable("groupInviteId") String groupInviteId, @PathVariable String userId) throws JsonProcessingException { return groupService.acceptInvitation(groupInviteId, userId); } @PostMapping(path = "/declineinvite/{groupInviteId}/{userId}") - @ApiOperation(value = "Decline an invitation to a group") + @Operation(summary = "Decline an invitation to a group") public ResponseEntity declineInvitation(@PathVariable("groupInviteId") String groupInviteId, @PathVariable String userId) throws JsonProcessingException { return groupService.declineInvitation(groupInviteId, userId); } diff --git a/src/main/java/com/example/holidayplanner/groupInvite/GroupInviteController.java b/src/main/java/com/example/holidayplanner/groupInvite/GroupInviteController.java index 5a330b4..21446b4 100644 --- a/src/main/java/com/example/holidayplanner/groupInvite/GroupInviteController.java +++ b/src/main/java/com/example/holidayplanner/groupInvite/GroupInviteController.java @@ -1,9 +1,9 @@ package com.example.holidayplanner.groupInvite; import com.fasterxml.jackson.core.JsonProcessingException; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -11,7 +11,7 @@ @RestController @RequestMapping("/api/v1.0/groupinvite") -@Api(tags = "Group Invite") +@Tag(name = "Group Invite") @SecurityRequirement(name = "holidayPlannerSecurity") public class GroupInviteController { private final GroupInviteService groupInviteService; @@ -21,13 +21,13 @@ public GroupInviteController(GroupInviteService groupInviteService) { } @GetMapping(path= "/{groupInviteId}") - @ApiOperation(value = "Find group invite by id") + @Operation(summary = "Find group invite by id") public ResponseEntity findById(@PathVariable("groupInviteId") String groupInviteId) throws JsonProcessingException { return groupInviteService.findById(groupInviteId); } @PostMapping(path = "/findmultiplebyid") - @ApiOperation(value = "Find multiple group invites by their ids") + @Operation(summary = "Find multiple group invites by their ids") public ResponseEntity findMultipleById(@RequestBody List groupInviteIds) throws JsonProcessingException { return groupInviteService.findMultipleById(groupInviteIds); } diff --git a/src/main/java/com/example/holidayplanner/holiday/HolidayController.java b/src/main/java/com/example/holidayplanner/holiday/HolidayController.java index 04fe449..0ef97df 100644 --- a/src/main/java/com/example/holidayplanner/holiday/HolidayController.java +++ b/src/main/java/com/example/holidayplanner/holiday/HolidayController.java @@ -1,9 +1,9 @@ package com.example.holidayplanner.holiday; import com.fasterxml.jackson.core.JsonProcessingException; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -13,7 +13,7 @@ @RestController @RequestMapping(path = "/api/v1.0/holidays") -@Api(tags = "Holiday") +@Tag(name = "Holiday") @SecurityRequirement(name = "holidayPlannerSecurity") public class HolidayController { @@ -40,25 +40,25 @@ public ResponseEntity getDateAndBudgetAggregates(@PathVariable("holidayId") Stri } @PostMapping(path = "/findmultiplebyid") - @ApiOperation(value = "Find multiple holidays by their ids") + @Operation(summary = "Find multiple holidays by their ids") public ResponseEntity findMultipleById(@RequestBody List holidayIds) throws JsonProcessingException { return holidayService.findMultipleById(holidayIds); } @GetMapping(path = "/findbyid/{holidayId}") - @ApiOperation(value = "Find a single holiday by its id") + @Operation(summary = "Find a single holiday by its id") public ResponseEntity findById(@PathVariable("holidayId") String holidayId) throws JsonProcessingException { return holidayService.findById(holidayId); } @GetMapping(path= "/acceptinvite/{holidayInviteId}/{userId}") - @ApiOperation(value = "Accept a holiday invite") + @Operation(summary = "Accept a holiday invite") public ResponseEntity acceptInvite(@PathVariable("holidayInviteId") String holidayInviteId, @PathVariable("userId") String userId) throws JsonProcessingException { return holidayService.acceptInvite(holidayInviteId, userId); } @GetMapping(path= "/declineinvite/{holidayInviteId}/{userId}") - @ApiOperation(value = "Decline a holiday invite") + @Operation(summary = "Decline a holiday invite") public ResponseEntity declineInvite(@PathVariable("holidayInviteId") String holidayInviteId, @PathVariable("userId") String userId) { return holidayService.declineInvite(holidayInviteId, userId); } diff --git a/src/main/java/com/example/holidayplanner/holidayInvite/HolidayInviteController.java b/src/main/java/com/example/holidayplanner/holidayInvite/HolidayInviteController.java index 6874421..f0ec026 100644 --- a/src/main/java/com/example/holidayplanner/holidayInvite/HolidayInviteController.java +++ b/src/main/java/com/example/holidayplanner/holidayInvite/HolidayInviteController.java @@ -1,9 +1,9 @@ package com.example.holidayplanner.holidayInvite; import com.fasterxml.jackson.core.JsonProcessingException; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -15,7 +15,7 @@ @RestController @RequestMapping("/api/v1.0/holidayinvite") -@Api(tags = "Holiday Invite") +@Tag(name = "Holiday Invite") @SecurityRequirement(name = "holidayPlannerSecurity") public class HolidayInviteController { @@ -27,7 +27,7 @@ public HolidayInviteController(HolidayInviteService holidayInviteService) { } @PostMapping("/findmultiplebyid") - @ApiOperation(value = "Find multiple holiday invites by their ids") + @Operation(summary = "Find multiple holiday invites by their ids") public ResponseEntity findMultipleById(@RequestBody List holidayInviteIds) throws JsonProcessingException { return holidayInviteService.findMultipleById(holidayInviteIds); } diff --git a/src/main/java/com/example/holidayplanner/holidayPreferences/HolidayPreferencesController.java b/src/main/java/com/example/holidayplanner/holidayPreferences/HolidayPreferencesController.java index 9a00bee..31292af 100644 --- a/src/main/java/com/example/holidayplanner/holidayPreferences/HolidayPreferencesController.java +++ b/src/main/java/com/example/holidayplanner/holidayPreferences/HolidayPreferencesController.java @@ -1,14 +1,14 @@ package com.example.holidayplanner.holidayPreferences; import com.fasterxml.jackson.core.JsonProcessingException; -import io.swagger.annotations.Api; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping(path = "/api/v1.0/holidaypreferences") -@Api(tags = "Holiday Preferences") +@Tag(name = "Holiday Preferences") @SecurityRequirement(name = "holidayPlannerSecurity") public class HolidayPreferencesController { diff --git a/src/main/java/com/example/holidayplanner/user/UserController.java b/src/main/java/com/example/holidayplanner/user/UserController.java index 592c505..f20a7f6 100644 --- a/src/main/java/com/example/holidayplanner/user/UserController.java +++ b/src/main/java/com/example/holidayplanner/user/UserController.java @@ -3,9 +3,9 @@ import com.example.holidayplanner.interfaces.ControllerInterface; import com.example.holidayplanner.scheduler.Scheduler; import com.fasterxml.jackson.core.JsonProcessingException; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; @@ -18,13 +18,13 @@ @RestController @RequestMapping(path = "/api/v1.0/users") -@Api(tags = "User") +@Tag(name = "User") @SecurityRequirement(name = "holidayPlannerSecurity") public class UserController implements ControllerInterface { private final UserService userService; - private Scheduler scheduler; + private final Scheduler scheduler; @Autowired public UserController(UserService userService, Scheduler scheduler) { @@ -34,7 +34,7 @@ public UserController(UserService userService, Scheduler scheduler) { @Override @PostMapping(path = "/newuser") - @ApiOperation(value = "Create a new user") + @Operation(summary = "Create a new user") public ResponseEntity create(@RequestBody @Valid User user, Errors errors) throws JsonProcessingException { if (errors.hasErrors()) { @@ -47,7 +47,7 @@ public ResponseEntity create(@RequestBody @Valid User user, Errors errors) throw } @PostMapping(path = "/login") - @ApiOperation(value = "Authenticate users") + @Operation(summary = "Authenticate users") @Transactional public ResponseEntity login (@RequestBody Map emailAndPassword) throws JsonProcessingException { @@ -56,73 +56,73 @@ public ResponseEntity login (@RequestBody Map emailAndPassword) @Override @GetMapping - @ApiOperation(value = "Get a list of all users") + @Operation(summary = "Get a list of all users") public List getAll() { return userService.getAll(); } @GetMapping(path="/sendfriendrequest/{userId}/{allegedFriendId}") - @ApiOperation(value = "Send a friend request") + @Operation(summary = "Send a friend request") public ResponseEntity sendFriendRequest(@PathVariable String userId, @PathVariable String allegedFriendId) { return userService.sendFriendRequest(userId, allegedFriendId); } @GetMapping(path="/acceptfriendrequest/{userId}/{friendId}") - @ApiOperation("Add a Friend") + @Operation(summary = "Add a Friend") public ResponseEntity acceptFriendRequest(@PathVariable("userId") String userId, @PathVariable("friendId") String friendId) { return userService.acceptFriendRequest(userId, friendId); } @GetMapping(path = "/declinefriendrequest/{userId}/{friendId}") - @ApiOperation("Delete a friend request") + @Operation(summary = "Delete a friend request") public ResponseEntity declineFriendRequest(@PathVariable("userId") String userId, @PathVariable("friendId") String friendId) { return userService.declineFriendRequest(userId, friendId); } @GetMapping(path= "/findbyid/{id}") - @ApiOperation(value = "Find a user by their id") + @Operation(summary = "Find a user by their id") public ResponseEntity findById(@PathVariable("id") String id) throws JsonProcessingException { return userService.findById(id); } @PostMapping(path="/findmultiplebyid") - @ApiOperation(value = "Find multiple users by their ids") + @Operation(summary = "Find multiple users by their ids") public ResponseEntity findMultipleById(@RequestBody List userIds) throws JsonProcessingException { return userService.findMultipleById(userIds); } @PostMapping(path="/findmultiplebyphonenumberoremail") - @ApiOperation(value = "Find multiple users by their phone numbers or email addresses") + @Operation(summary = "Find multiple users by their phone numbers or email addresses") public ResponseEntity findMultipleByPhoneNumberOrEmail(@RequestBody Map> phoneNumbersAndEmails) throws JsonProcessingException { return userService.findMultipleByPhoneNumberOrEmail(phoneNumbersAndEmails); } @GetMapping(path = "/search/{searchTerm}") - @ApiOperation(value = "Search for a user") + @Operation(summary = "Search for a user") public ResponseEntity> search(@PathVariable("searchTerm") String searchTerm) throws JsonProcessingException { return userService.search(searchTerm); } @PostMapping(path = "savedevicetoken/{userId}") - @ApiOperation(value = "Save a user's device token") + @Operation(summary = "Save a user's device token") public ResponseEntity saveDeviceToken(@PathVariable("userId") String userId, @RequestBody String deviceToken) { return userService.saveDeviceToken(userId, deviceToken); } @Override @PutMapping (path = "/{userId}") - @ApiOperation(value = "Update user details") + @Operation(summary = "Update user details") public String update(@PathVariable("userId") String userId, @RequestBody User newUserInfo) { return userService.update(userId, newUserInfo); } @Override @DeleteMapping(path = "/{userId}") - @ApiOperation(value = "Delete a user") + @Operation(summary = "Delete a user") public String delete(@PathVariable("userId") String userId) { return userService.delete(userId); } @GetMapping(path = "/updateuserproperties") - @ApiOperation(value = "Update user properties in database") + @Operation(summary = "Update user properties in database") public ResponseEntity updateUserproperties() { scheduler.updateUserPropertiesIfNotPresent(); return ResponseEntity.ok().build(); diff --git a/src/main/java/com/example/holidayplanner/user/UserService.java b/src/main/java/com/example/holidayplanner/user/UserService.java index dcf0886..d1c78ca 100644 --- a/src/main/java/com/example/holidayplanner/user/UserService.java +++ b/src/main/java/com/example/holidayplanner/user/UserService.java @@ -110,9 +110,11 @@ public ResponseEntity create(User user) throws JsonProcessingException { final String refreshToken = jwtTokenUtil.generateRefreshToken(userDetails); Token token = new Token(); - token.setUserId(user.getId()); + token.setOwner(user); token.setAccessToken(accessToken); + token.setAccessTokenExpiration(jwtTokenUtil.extractExpiration(accessToken)); token.setRefreshToken(refreshToken); + token.setRefreshTokenExpiration(jwtTokenUtil.extractExpiration(refreshToken)); tokenService.saveToken(token); @@ -149,9 +151,11 @@ public ResponseEntity login(Map emailAndPassword) throws final String refreshToken = jwtTokenUtil.generateRefreshToken(userDetails); Token token = new Token(); - token.setUserId(user.getId()); + token.setOwner(user); token.setAccessToken(accessToken); + token.setAccessTokenExpiration(jwtTokenUtil.extractExpiration(accessToken)); token.setRefreshToken(refreshToken); + token.setRefreshTokenExpiration(jwtTokenUtil.extractExpiration(refreshToken)); tokenService.saveToken(token); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1e83f95..7fe686a 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,2 +1,3 @@ spring.mvc.pathmatch.matching-strategy=ant-path-matcher -spring.batch.job.enabled=false \ No newline at end of file +spring.batch.job.enabled=false +springdoc.swagger-ui.use-root-path=true