diff --git a/pom.xml b/pom.xml index 4b6fd187..09aef31d 100644 --- a/pom.xml +++ b/pom.xml @@ -141,6 +141,13 @@ commons-lang3 + + org.springdoc + springdoc-openapi-ui + 1.8.0 + + + diff --git a/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java b/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java index 6e45ce6b..b3139fd4 100644 --- a/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java +++ b/src/main/java/com/sopromadze/blogapi/BlogApiApplication.java @@ -1,6 +1,8 @@ package com.sopromadze.blogapi; import com.sopromadze.blogapi.security.JwtAuthenticationFilter; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.info.Info; import org.modelmapper.ModelMapper; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -11,28 +13,37 @@ import javax.annotation.PostConstruct; import java.util.TimeZone; +@OpenAPIDefinition( + info = @Info( + title = "Spring-Boot-Blog-REST-API", + description = "A RESTful webservice for managing blog posts, built with Spring Boot. Includes JWT-based authentication and MySQL for data storage.", + summary = "Build Restful CRUD API for a blog using Spring Boot, Mysql, JPA and Hibernate.", + version = "1.0.1" + ) +) + @SpringBootApplication -@EntityScan(basePackageClasses = { BlogApiApplication.class, Jsr310Converters.class }) +@EntityScan(basePackageClasses = {BlogApiApplication.class, Jsr310Converters.class}) public class BlogApiApplication { - public static void main(String[] args) { - SpringApplication.run(BlogApiApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(BlogApiApplication.class, args); + } - @PostConstruct - void init() { - TimeZone.setDefault(TimeZone.getTimeZone("UTC")); - } + @PostConstruct + void init() { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + } - @Bean - public JwtAuthenticationFilter jwtAuthenticationFilter() { - return new JwtAuthenticationFilter(); - } + @Bean + public JwtAuthenticationFilter jwtAuthenticationFilter() { + return new JwtAuthenticationFilter(); + } - @Bean - public ModelMapper modelMapper() { - return new ModelMapper(); - } + @Bean + public ModelMapper modelMapper() { + return new ModelMapper(); + } -} +} \ No newline at end of file diff --git a/src/main/java/com/sopromadze/blogapi/config/SecutiryConfig.java b/src/main/java/com/sopromadze/blogapi/config/SecutiryConfig.java index 1a0c1175..b4556f5d 100644 --- a/src/main/java/com/sopromadze/blogapi/config/SecutiryConfig.java +++ b/src/main/java/com/sopromadze/blogapi/config/SecutiryConfig.java @@ -51,6 +51,7 @@ protected void configure(HttpSecurity http) throws Exception { .and() .authorizeRequests() .antMatchers(HttpMethod.GET, "/api/**").permitAll() + .antMatchers(HttpMethod.GET, "/swagger-ui/**", "/v3/api-docs/**").permitAll() .antMatchers(HttpMethod.POST, "/api/auth/**").permitAll() .antMatchers(HttpMethod.GET, "/api/users/checkUsernameAvailability", "/api/users/checkEmailAvailability").permitAll() .anyRequest().authenticated(); diff --git a/src/main/java/com/sopromadze/blogapi/controller/AlbumController.java b/src/main/java/com/sopromadze/blogapi/controller/AlbumController.java index b5dc9ff4..47be4295 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/AlbumController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/AlbumController.java @@ -13,6 +13,8 @@ import com.sopromadze.blogapi.service.PhotoService; import com.sopromadze.blogapi.utils.AppConstants; import com.sopromadze.blogapi.utils.AppUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -30,61 +32,69 @@ import javax.validation.Valid; +@Tag(name = "5- Albums", description = "Operations related to albums") + @RestController @RequestMapping("/api/albums") public class AlbumController { - @Autowired - private AlbumService albumService; - - @Autowired - private PhotoService photoService; - - @ExceptionHandler(ResponseEntityErrorException.class) - public ResponseEntity handleExceptions(ResponseEntityErrorException exception) { - return exception.getApiResponse(); - } - - @GetMapping - public PagedResponse getAllAlbums( - @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, - @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) { - AppUtils.validatePageNumberAndSize(page, size); - - return albumService.getAllAlbums(page, size); - } - - @PostMapping - @PreAuthorize("hasRole('USER')") - public ResponseEntity addAlbum(@Valid @RequestBody AlbumRequest albumRequest, @CurrentUser UserPrincipal currentUser) { - return albumService.addAlbum(albumRequest, currentUser); - } - - @GetMapping("/{id}") - public ResponseEntity getAlbum(@PathVariable(name = "id") Long id) { - return albumService.getAlbum(id); - } - - @PutMapping("/{id}") - @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") - public ResponseEntity updateAlbum(@PathVariable(name = "id") Long id, @Valid @RequestBody AlbumRequest newAlbum, - @CurrentUser UserPrincipal currentUser) { - return albumService.updateAlbum(id, newAlbum, currentUser); - } - - @DeleteMapping("/{id}") - @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") - public ResponseEntity deleteAlbum(@PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser) { - return albumService.deleteAlbum(id, currentUser); - } - - @GetMapping("/{id}/photos") - public ResponseEntity> getAllPhotosByAlbum(@PathVariable(name = "id") Long id, - @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, - @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) { - - PagedResponse response = photoService.getAllPhotosByAlbum(id, page, size); - - return new ResponseEntity<>(response, HttpStatus.OK); - } + @Autowired + private AlbumService albumService; + + @Autowired + private PhotoService photoService; + + @ExceptionHandler(ResponseEntityErrorException.class) + public ResponseEntity handleExceptions(ResponseEntityErrorException exception) { + return exception.getApiResponse(); + } + + @Operation(description = "Get all albums", summary = "Get albums") + @GetMapping + public PagedResponse getAllAlbums( + @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, + @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) { + AppUtils.validatePageNumberAndSize(page, size); + + return albumService.getAllAlbums(page, size); + } + + @Operation(description = "Create new album (By logged in user)", summary = "Create album") + @PostMapping + @PreAuthorize("hasRole('USER')") + public ResponseEntity addAlbum(@Valid @RequestBody AlbumRequest albumRequest, @CurrentUser UserPrincipal currentUser) { + return albumService.addAlbum(albumRequest, currentUser); + } + + @Operation(description = "Get album by id", summary = "Get album") + @GetMapping("/{id}") + public ResponseEntity getAlbum(@PathVariable(name = "id") Long id) { + return albumService.getAlbum(id); + } + + @Operation(description = "Update album (If album belongs to logged in user or logged in user is admin)", summary = "Update album") + @PutMapping("/{id}") + @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") + public ResponseEntity updateAlbum(@PathVariable(name = "id") Long id, @Valid @RequestBody AlbumRequest newAlbum, + @CurrentUser UserPrincipal currentUser) { + return albumService.updateAlbum(id, newAlbum, currentUser); + } + + @Operation(description = "Delete album (If album belongs to logged in user or logged in user is admin)", summary = "Delete album") + @DeleteMapping("/{id}") + @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") + public ResponseEntity deleteAlbum(@PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser) { + return albumService.deleteAlbum(id, currentUser); + } + + @Operation(description = "Get all photos which belongs to album with id = id", summary = "Get photos") + @GetMapping("/{id}/photos") + public ResponseEntity> getAllPhotosByAlbum(@PathVariable(name = "id") Long id, + @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, + @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) { + + PagedResponse response = photoService.getAllPhotosByAlbum(id, page, size); + + return new ResponseEntity<>(response, HttpStatus.OK); + } } diff --git a/src/main/java/com/sopromadze/blogapi/controller/AuthController.java b/src/main/java/com/sopromadze/blogapi/controller/AuthController.java index 5d49753d..a93639d8 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/AuthController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/AuthController.java @@ -12,6 +12,8 @@ import com.sopromadze.blogapi.repository.RoleRepository; import com.sopromadze.blogapi.repository.UserRepository; import com.sopromadze.blogapi.security.JwtTokenProvider; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -31,78 +33,82 @@ import java.util.ArrayList; import java.util.List; +@Tag(name = "1- Auth", description = "Operations related to sign in and sign up") + @RestController @RequestMapping("/api/auth") public class AuthController { - private static final String USER_ROLE_NOT_SET = "User role not set"; + private static final String USER_ROLE_NOT_SET = "User role not set"; - @Autowired - private AuthenticationManager authenticationManager; + @Autowired + private AuthenticationManager authenticationManager; - @Autowired - private UserRepository userRepository; + @Autowired + private UserRepository userRepository; - @Autowired - private RoleRepository roleRepository; + @Autowired + private RoleRepository roleRepository; - @Autowired - private PasswordEncoder passwordEncoder; + @Autowired + private PasswordEncoder passwordEncoder; - @Autowired - private JwtTokenProvider jwtTokenProvider; + @Autowired + private JwtTokenProvider jwtTokenProvider; - @PostMapping("/signin") - public ResponseEntity authenticateUser(@Valid @RequestBody LoginRequest loginRequest) { - Authentication authentication = authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken(loginRequest.getUsernameOrEmail(), loginRequest.getPassword())); + @Operation(description = "Log in", summary = "Log in / sign in") + @PostMapping("/signin") + public ResponseEntity authenticateUser(@Valid @RequestBody LoginRequest loginRequest) { + Authentication authentication = authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(loginRequest.getUsernameOrEmail(), loginRequest.getPassword())); - SecurityContextHolder.getContext().setAuthentication(authentication); + SecurityContextHolder.getContext().setAuthentication(authentication); - String jwt = jwtTokenProvider.generateToken(authentication); - return ResponseEntity.ok(new JwtAuthenticationResponse(jwt)); - } + String jwt = jwtTokenProvider.generateToken(authentication); + return ResponseEntity.ok(new JwtAuthenticationResponse(jwt)); + } - @PostMapping("/signup") - public ResponseEntity registerUser(@Valid @RequestBody SignUpRequest signUpRequest) { - if (Boolean.TRUE.equals(userRepository.existsByUsername(signUpRequest.getUsername()))) { - throw new BlogapiException(HttpStatus.BAD_REQUEST, "Username is already taken"); - } + @Operation(description = "Sign up", summary = "Sign up") + @PostMapping("/signup") + public ResponseEntity registerUser(@Valid @RequestBody SignUpRequest signUpRequest) { + if (Boolean.TRUE.equals(userRepository.existsByUsername(signUpRequest.getUsername()))) { + throw new BlogapiException(HttpStatus.BAD_REQUEST, "Username is already taken"); + } - if (Boolean.TRUE.equals(userRepository.existsByEmail(signUpRequest.getEmail()))) { - throw new BlogapiException(HttpStatus.BAD_REQUEST, "Email is already taken"); - } + if (Boolean.TRUE.equals(userRepository.existsByEmail(signUpRequest.getEmail()))) { + throw new BlogapiException(HttpStatus.BAD_REQUEST, "Email is already taken"); + } - String firstName = signUpRequest.getFirstName().toLowerCase(); + String firstName = signUpRequest.getFirstName().toLowerCase(); - String lastName = signUpRequest.getLastName().toLowerCase(); + String lastName = signUpRequest.getLastName().toLowerCase(); - String username = signUpRequest.getUsername().toLowerCase(); + String username = signUpRequest.getUsername().toLowerCase(); - String email = signUpRequest.getEmail().toLowerCase(); + String email = signUpRequest.getEmail().toLowerCase(); - String password = passwordEncoder.encode(signUpRequest.getPassword()); + String password = passwordEncoder.encode(signUpRequest.getPassword()); - User user = new User(firstName, lastName, username, email, password); + User user = new User(firstName, lastName, username, email, password); - List roles = new ArrayList<>(); + List roles = new ArrayList<>(); - if (userRepository.count() == 0) { - roles.add(roleRepository.findByName(RoleName.ROLE_USER) - .orElseThrow(() -> new AppException(USER_ROLE_NOT_SET))); - roles.add(roleRepository.findByName(RoleName.ROLE_ADMIN) - .orElseThrow(() -> new AppException(USER_ROLE_NOT_SET))); - } else { - roles.add(roleRepository.findByName(RoleName.ROLE_USER) - .orElseThrow(() -> new AppException(USER_ROLE_NOT_SET))); - } + if (userRepository.count() == 0) { + roles.add(roleRepository.findByName(RoleName.ROLE_USER) + .orElseThrow(() -> new AppException(USER_ROLE_NOT_SET))); + roles.add(roleRepository.findByName(RoleName.ROLE_ADMIN) + .orElseThrow(() -> new AppException(USER_ROLE_NOT_SET))); + } else { + roles.add(roleRepository.findByName(RoleName.ROLE_USER) + .orElseThrow(() -> new AppException(USER_ROLE_NOT_SET))); + } - user.setRoles(roles); + user.setRoles(roles); - User result = userRepository.save(user); + User result = userRepository.save(user); - URI location = ServletUriComponentsBuilder.fromCurrentContextPath().path("/api/users/{userId}") - .buildAndExpand(result.getId()).toUri(); + URI location = ServletUriComponentsBuilder.fromCurrentContextPath().path("/api/users/{userId}") + .buildAndExpand(result.getId()).toUri(); - return ResponseEntity.created(location).body(new ApiResponse(Boolean.TRUE, "User registered successfully")); - } + return ResponseEntity.created(location).body(new ApiResponse(Boolean.TRUE, "User registered successfully")); + } } diff --git a/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java b/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java index 39b63842..9479899f 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/CategoryController.java @@ -8,6 +8,8 @@ import com.sopromadze.blogapi.security.UserPrincipal; import com.sopromadze.blogapi.service.CategoryService; import com.sopromadze.blogapi.utils.AppConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -23,44 +25,50 @@ import javax.validation.Valid; +@Tag(name = "7- Categories", description = "Operations related to categories") @RestController @RequestMapping("/api/categories") public class CategoryController { - @Autowired - private CategoryService categoryService; + @Autowired + private CategoryService categoryService; - @GetMapping - public PagedResponse getAllCategories( - @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, - @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) { - return categoryService.getAllCategories(page, size); - } + @Operation(description = "Get all categories", summary = "Get all categories") + @GetMapping + public PagedResponse getAllCategories( + @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, + @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) { + return categoryService.getAllCategories(page, size); + } - @PostMapping - @PreAuthorize("hasRole('USER')") - public ResponseEntity addCategory(@Valid @RequestBody Category category, - @CurrentUser UserPrincipal currentUser) { + @Operation(description = "Create a category (Only for logged in user)", summary = "Create category") + @PostMapping + @PreAuthorize("hasRole('USER')") + public ResponseEntity addCategory(@Valid @RequestBody Category category, + @CurrentUser UserPrincipal currentUser) { - return categoryService.addCategory(category, currentUser); - } + return categoryService.addCategory(category, currentUser); + } - @GetMapping("/{id}") - public ResponseEntity getCategory(@PathVariable(name = "id") Long id) { - return categoryService.getCategory(id); - } + @Operation(description = "Get category by id", summary = "Get category") + @GetMapping("/{id}") + public ResponseEntity getCategory(@PathVariable(name = "id") Long id) { + return categoryService.getCategory(id); + } - @PutMapping("/{id}") - @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") - public ResponseEntity updateCategory(@PathVariable(name = "id") Long id, - @Valid @RequestBody Category category, @CurrentUser UserPrincipal currentUser) throws UnauthorizedException { - return categoryService.updateCategory(id, category, currentUser); - } + @Operation(description = "Update category (Only for logged in user or admin)", summary = "Update category") + @PutMapping("/{id}") + @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") + public ResponseEntity updateCategory(@PathVariable(name = "id") Long id, + @Valid @RequestBody Category category, @CurrentUser UserPrincipal currentUser) throws UnauthorizedException { + return categoryService.updateCategory(id, category, currentUser); + } - @DeleteMapping("/{id}") - @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") - public ResponseEntity deleteCategory(@PathVariable(name = "id") Long id, - @CurrentUser UserPrincipal currentUser) throws UnauthorizedException { - return categoryService.deleteCategory(id, currentUser); - } + @Operation(description = "Delete category (Only for logged in user or admin)", summary = "Delete category") + @DeleteMapping("/{id}") + @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") + public ResponseEntity deleteCategory(@PathVariable(name = "id") Long id, + @CurrentUser UserPrincipal currentUser) throws UnauthorizedException { + return categoryService.deleteCategory(id, currentUser); + } } diff --git a/src/main/java/com/sopromadze/blogapi/controller/CommentController.java b/src/main/java/com/sopromadze/blogapi/controller/CommentController.java index 2a402910..b39f2bc3 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/CommentController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/CommentController.java @@ -8,6 +8,8 @@ import com.sopromadze.blogapi.security.UserPrincipal; import com.sopromadze.blogapi.service.CommentService; import com.sopromadze.blogapi.utils.AppConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -24,60 +26,69 @@ import javax.validation.Valid; +@Tag(name = "4- Comments", description = "Operations related to comments blog posts") + @RestController @RequestMapping("/api/posts/{postId}/comments") public class CommentController { - @Autowired - private CommentService commentService; + @Autowired + private CommentService commentService; - @GetMapping - public ResponseEntity> getAllComments(@PathVariable(name = "postId") Long postId, - @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, - @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) { + @Operation(description = "Get all comments which belongs to post with id = postId", summary = "Get comments") + @GetMapping + public ResponseEntity> getAllComments(@PathVariable(name = "postId") Long postId, + @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, + @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) { - PagedResponse allComments = commentService.getAllComments(postId, page, size); + PagedResponse allComments = commentService.getAllComments(postId, page, size); - return new ResponseEntity< >(allComments, HttpStatus.OK); - } + return new ResponseEntity<>(allComments, HttpStatus.OK); + } - @PostMapping - @PreAuthorize("hasRole('USER')") - public ResponseEntity addComment(@Valid @RequestBody CommentRequest commentRequest, - @PathVariable(name = "postId") Long postId, @CurrentUser UserPrincipal currentUser) { - Comment newComment = commentService.addComment(commentRequest, postId, currentUser); + @Operation(description = "Create new comment for post with id = postId (By logged in user)", summary = "Create comment") + @PostMapping + @PreAuthorize("hasRole('USER')") + public ResponseEntity addComment(@Valid @RequestBody CommentRequest commentRequest, + @PathVariable(name = "postId") Long postId, @CurrentUser UserPrincipal currentUser) { + Comment newComment = commentService.addComment(commentRequest, postId, currentUser); - return new ResponseEntity<>(newComment, HttpStatus.CREATED); - } + return new ResponseEntity<>(newComment, HttpStatus.CREATED); + } - @GetMapping("/{id}") - public ResponseEntity getComment(@PathVariable(name = "postId") Long postId, - @PathVariable(name = "id") Long id) { - Comment comment = commentService.getComment(postId, id); + @Operation(description = "Get comment by id if it belongs to post with id = postId", summary = "Get Comment") + @GetMapping("/{id}") + public ResponseEntity getComment(@PathVariable(name = "postId") Long postId, + @PathVariable(name = "id") Long id) { + Comment comment = commentService.getComment(postId, id); - return new ResponseEntity<>(comment, HttpStatus.OK); - } + return new ResponseEntity<>(comment, HttpStatus.OK); + } - @PutMapping("/{id}") - @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") - public ResponseEntity updateComment(@PathVariable(name = "postId") Long postId, - @PathVariable(name = "id") Long id, @Valid @RequestBody CommentRequest commentRequest, - @CurrentUser UserPrincipal currentUser) { + @Operation(description = "Update comment by id if it belongs to post with id = postId (If comment belongs to logged in user or logged in user is admin)", + summary = "Update comment") + @PutMapping("/{id}") + @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") + public ResponseEntity updateComment(@PathVariable(name = "postId") Long postId, + @PathVariable(name = "id") Long id, @Valid @RequestBody CommentRequest commentRequest, + @CurrentUser UserPrincipal currentUser) { - Comment updatedComment = commentService.updateComment(postId, id, commentRequest, currentUser); + Comment updatedComment = commentService.updateComment(postId, id, commentRequest, currentUser); - return new ResponseEntity<>(updatedComment, HttpStatus.OK); - } + return new ResponseEntity<>(updatedComment, HttpStatus.OK); + } - @DeleteMapping("/{id}") - @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") - public ResponseEntity deleteComment(@PathVariable(name = "postId") Long postId, - @PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser) { + @Operation(description = "Delete comment by id if it belongs to post with id = postId (If comment belongs to logged in user or logged in user is admin)", + summary = "Delete comment") + @DeleteMapping("/{id}") + @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") + public ResponseEntity deleteComment(@PathVariable(name = "postId") Long postId, + @PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser) { - ApiResponse response = commentService.deleteComment(postId, id, currentUser); + ApiResponse response = commentService.deleteComment(postId, id, currentUser); - HttpStatus status = response.getSuccess() ? HttpStatus.OK : HttpStatus.BAD_REQUEST; + HttpStatus status = response.getSuccess() ? HttpStatus.OK : HttpStatus.BAD_REQUEST; - return new ResponseEntity<>(response, status); - } + return new ResponseEntity<>(response, status); + } } diff --git a/src/main/java/com/sopromadze/blogapi/controller/PhotoController.java b/src/main/java/com/sopromadze/blogapi/controller/PhotoController.java index 2f0846d0..64aa29f6 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/PhotoController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/PhotoController.java @@ -8,6 +8,8 @@ import com.sopromadze.blogapi.security.UserPrincipal; import com.sopromadze.blogapi.service.PhotoService; import com.sopromadze.blogapi.utils.AppConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -24,12 +26,14 @@ import javax.validation.Valid; +@Tag(name = "6- Photos", description = "Operations related to photos") @RestController @RequestMapping("/api/photos") public class PhotoController { @Autowired private PhotoService photoService; + @Operation(description = "Get all photos", summary = "Get photos") @GetMapping public PagedResponse getAllPhotos( @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, @@ -37,6 +41,7 @@ public PagedResponse getAllPhotos( return photoService.getAllPhotos(page, size); } + @Operation(description = "Create new photo (By logged in user)", summary = "Create photo") @PostMapping @PreAuthorize("hasRole('USER')") public ResponseEntity addPhoto(@Valid @RequestBody PhotoRequest photoRequest, @@ -46,6 +51,7 @@ public ResponseEntity addPhoto(@Valid @RequestBody PhotoRequest p return new ResponseEntity< >(photoResponse, HttpStatus.OK); } + @Operation(description = "Get photo by id", summary = "Get photo") @GetMapping("/{id}") public ResponseEntity getPhoto(@PathVariable(name = "id") Long id) { PhotoResponse photoResponse = photoService.getPhoto(id); @@ -53,6 +59,7 @@ public ResponseEntity getPhoto(@PathVariable(name = "id") Long id return new ResponseEntity< >(photoResponse, HttpStatus.OK); } + @Operation(description = "Update photo (If photo belongs to logged in user or logged in user is admin", summary = "Update photo") @PutMapping("/{id}") @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") public ResponseEntity updatePhoto(@PathVariable(name = "id") Long id, @@ -63,6 +70,7 @@ public ResponseEntity updatePhoto(@PathVariable(name = "id") Long return new ResponseEntity< >(photoResponse, HttpStatus.OK); } + @Operation(description = "Delete photo (If photo belongs to logged in user or logged in user is admin)", summary = "Delete photo") @DeleteMapping("/{id}") @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") public ResponseEntity deletePhoto(@PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser) { diff --git a/src/main/java/com/sopromadze/blogapi/controller/PostController.java b/src/main/java/com/sopromadze/blogapi/controller/PostController.java index 5b8efcc7..e00f7d04 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/PostController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/PostController.java @@ -9,6 +9,8 @@ import com.sopromadze.blogapi.security.UserPrincipal; import com.sopromadze.blogapi.service.PostService; import com.sopromadze.blogapi.utils.AppConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -25,12 +27,15 @@ import javax.validation.Valid; +@Tag(name = "3- Posts", description = "Operations related to blog posts") + @RestController @RequestMapping("/api/posts") public class PostController { @Autowired private PostService postService; + @Operation(description = "Get all posts", summary = "Get all posts") @GetMapping public ResponseEntity> getAllPosts( @RequestParam(value = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, @@ -40,6 +45,7 @@ public ResponseEntity> getAllPosts( return new ResponseEntity< >(response, HttpStatus.OK); } + @Operation(description = "Get post by category id", summary = "Get posts by category", hidden = true) @GetMapping("/category/{id}") public ResponseEntity> getPostsByCategory( @RequestParam(value = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, @@ -50,6 +56,7 @@ public ResponseEntity> getPostsByCategory( return new ResponseEntity< >(response, HttpStatus.OK); } + @Operation(description = "Get all posts by tag", summary = "Get all posts by tag", hidden = true) @GetMapping("/tag/{id}") public ResponseEntity> getPostsByTag( @RequestParam(value = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, @@ -60,6 +67,7 @@ public ResponseEntity> getPostsByTag( return new ResponseEntity< >(response, HttpStatus.OK); } + @Operation(description = "Create new post (By logged in user)", summary = "Create post") @PostMapping @PreAuthorize("hasRole('USER')") public ResponseEntity addPost(@Valid @RequestBody PostRequest postRequest, @@ -69,6 +77,7 @@ public ResponseEntity addPost(@Valid @RequestBody PostRequest post return new ResponseEntity< >(postResponse, HttpStatus.CREATED); } + @Operation(description = "Get post by id", summary = "Get post") @GetMapping("/{id}") public ResponseEntity getPost(@PathVariable(name = "id") Long id) { Post post = postService.getPost(id); @@ -76,6 +85,7 @@ public ResponseEntity getPost(@PathVariable(name = "id") Long id) { return new ResponseEntity< >(post, HttpStatus.OK); } + @Operation(description = "Update post (If post belongs to logged in user or logged in user is admin)", summary = "Update post") @PutMapping("/{id}") @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") public ResponseEntity updatePost(@PathVariable(name = "id") Long id, @@ -85,6 +95,7 @@ public ResponseEntity updatePost(@PathVariable(name = "id") Long id, return new ResponseEntity< >(post, HttpStatus.OK); } + @Operation(description = "Delete post (If post belongs to logged in user or logged in user is admin)", summary = "Delete post") @DeleteMapping("/{id}") @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") public ResponseEntity deletePost(@PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser) { diff --git a/src/main/java/com/sopromadze/blogapi/controller/TagController.java b/src/main/java/com/sopromadze/blogapi/controller/TagController.java index 9a9b45c8..91f74717 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/TagController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/TagController.java @@ -7,6 +7,7 @@ import com.sopromadze.blogapi.security.UserPrincipal; import com.sopromadze.blogapi.service.TagService; import com.sopromadze.blogapi.utils.AppConstants; +import io.swagger.v3.oas.annotations.Operation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -23,52 +24,58 @@ import javax.validation.Valid; +@io.swagger.v3.oas.annotations.tags.Tag(name = "8- Tags", description = "Operations related to tags") @RestController @RequestMapping("/api/tags") public class TagController { - @Autowired - private TagService tagService; + @Autowired + private TagService tagService; - @GetMapping - public ResponseEntity> getAllTags( - @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, - @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) { + @Operation(description = "Get all tags", summary = "Get tags") + @GetMapping + public ResponseEntity> getAllTags( + @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, + @RequestParam(name = "size", required = false, defaultValue = AppConstants.DEFAULT_PAGE_SIZE) Integer size) { - PagedResponse response = tagService.getAllTags(page, size); + PagedResponse response = tagService.getAllTags(page, size); - return new ResponseEntity< >(response, HttpStatus.OK); - } + return new ResponseEntity<>(response, HttpStatus.OK); + } - @PostMapping - @PreAuthorize("hasRole('USER')") - public ResponseEntity addTag(@Valid @RequestBody Tag tag, @CurrentUser UserPrincipal currentUser) { - Tag newTag = tagService.addTag(tag, currentUser); + @Operation(description = "Create tag (Only for logged in user)", summary = "Create tag") + @PostMapping + @PreAuthorize("hasRole('USER')") + public ResponseEntity addTag(@Valid @RequestBody Tag tag, @CurrentUser UserPrincipal currentUser) { + Tag newTag = tagService.addTag(tag, currentUser); - return new ResponseEntity< >(newTag, HttpStatus.CREATED); - } + return new ResponseEntity<>(newTag, HttpStatus.CREATED); + } - @GetMapping("/{id}") - public ResponseEntity getTag(@PathVariable(name = "id") Long id) { - Tag tag = tagService.getTag(id); + @Operation(description = "Get tag", summary = "Get tag") + @GetMapping("/{id}") + public ResponseEntity getTag(@PathVariable(name = "id") Long id) { + Tag tag = tagService.getTag(id); - return new ResponseEntity< >(tag, HttpStatus.OK); - } + return new ResponseEntity<>(tag, HttpStatus.OK); + } - @PutMapping("/{id}") - @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") - public ResponseEntity updateTag(@PathVariable(name = "id") Long id, @Valid @RequestBody Tag tag, @CurrentUser UserPrincipal currentUser) { + @Operation(description = "Update tag (only for logged in user or admin", summary = "Update tag") + @PutMapping("/{id}") + @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") + public ResponseEntity updateTag(@PathVariable(name = "id") Long id, @Valid @RequestBody Tag tag, @CurrentUser UserPrincipal currentUser) { - Tag updatedTag = tagService.updateTag(id, tag, currentUser); + Tag updatedTag = tagService.updateTag(id, tag, currentUser); - return new ResponseEntity< >(updatedTag, HttpStatus.OK); - } + return new ResponseEntity<>(updatedTag, HttpStatus.OK); + } - @DeleteMapping("/{id}") - @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") - public ResponseEntity deleteTag(@PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser) { - ApiResponse apiResponse = tagService.deleteTag(id, currentUser); + @Operation(description = "Delete tag (Only for logged in user or admin", summary = "Delete tag") + @DeleteMapping("/{id}") + @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") + public ResponseEntity deleteTag(@PathVariable(name = "id") Long id, @CurrentUser UserPrincipal currentUser) { + ApiResponse apiResponse = tagService.deleteTag(id, currentUser); - return new ResponseEntity< >(apiResponse, HttpStatus.OK); - } + return new ResponseEntity<>(apiResponse, HttpStatus.OK); + } } diff --git a/src/main/java/com/sopromadze/blogapi/controller/TodoController.java b/src/main/java/com/sopromadze/blogapi/controller/TodoController.java index aeb57789..64d0637e 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/TodoController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/TodoController.java @@ -7,6 +7,8 @@ import com.sopromadze.blogapi.security.UserPrincipal; import com.sopromadze.blogapi.service.TodoService; import com.sopromadze.blogapi.utils.AppConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -23,6 +25,7 @@ import javax.validation.Valid; +@Tag(name = "9- Todos", description = "Operations related to todos") @RestController @RequestMapping("/api/todos") public class TodoController { @@ -30,6 +33,7 @@ public class TodoController { @Autowired private TodoService todoService; + @Operation(description = "Get all todos which belongs to logged in user", summary = "Get all todos") @GetMapping @PreAuthorize("hasRole('USER')") public ResponseEntity> getAllTodos( @@ -42,6 +46,7 @@ public ResponseEntity> getAllTodos( return new ResponseEntity< >(response, HttpStatus.OK); } + @Operation(description = "Create new todo (By logged in user)", summary = "Create todo") @PostMapping @PreAuthorize("hasRole('USER')") public ResponseEntity addTodo(@Valid @RequestBody Todo todo, @CurrentUser UserPrincipal currentUser) { @@ -50,6 +55,7 @@ public ResponseEntity addTodo(@Valid @RequestBody Todo todo, @CurrentUser return new ResponseEntity< >(newTodo, HttpStatus.CREATED); } + @Operation(description = "Get todo by id (If todo belongs to logged in user)", summary = "Get todo") @GetMapping("/{id}") @PreAuthorize("hasRole('USER')") public ResponseEntity getTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser) { @@ -58,6 +64,7 @@ public ResponseEntity getTodo(@PathVariable(value = "id") Long id, @Curren return new ResponseEntity< >(todo, HttpStatus.OK); } + @Operation(description = "Update todo (If todo belongs to logged in user)", summary = "Update todo") @PutMapping("/{id}") @PreAuthorize("hasRole('USER')") public ResponseEntity updateTodo(@PathVariable(value = "id") Long id, @Valid @RequestBody Todo newTodo, @@ -67,6 +74,7 @@ public ResponseEntity updateTodo(@PathVariable(value = "id") Long id, @Val return new ResponseEntity< >(updatedTodo, HttpStatus.OK); } + @Operation(description = "Delete todo (If todo belongs to logged in user)", summary = "delete todo") @DeleteMapping("/{id}") @PreAuthorize("hasRole('USER')") public ResponseEntity deleteTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser) { @@ -75,6 +83,7 @@ public ResponseEntity deleteTodo(@PathVariable(value = "id") Long i return new ResponseEntity<>(apiResponse, HttpStatus.OK); } + @Operation(description = "Mark todo as complete (If todo belongs to logged in user)", summary = "Complete todo") @PutMapping("/{id}/complete") @PreAuthorize("hasRole('USER')") public ResponseEntity completeTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser) { @@ -84,6 +93,7 @@ public ResponseEntity completeTodo(@PathVariable(value = "id") Long id, @C return new ResponseEntity< >(todo, HttpStatus.OK); } + @Operation(description = "Mark todo as incomplete (If todo belongs to logged in user)", summary = "Incomplete todo") @PutMapping("/{id}/unComplete") @PreAuthorize("hasRole('USER')") public ResponseEntity unCompleteTodo(@PathVariable(value = "id") Long id, @CurrentUser UserPrincipal currentUser) { diff --git a/src/main/java/com/sopromadze/blogapi/controller/UserController.java b/src/main/java/com/sopromadze/blogapi/controller/UserController.java index f6b717e2..e8440a8a 100644 --- a/src/main/java/com/sopromadze/blogapi/controller/UserController.java +++ b/src/main/java/com/sopromadze/blogapi/controller/UserController.java @@ -15,6 +15,8 @@ import com.sopromadze.blogapi.service.PostService; import com.sopromadze.blogapi.service.UserService; import com.sopromadze.blogapi.utils.AppConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -31,6 +33,7 @@ import javax.validation.Valid; +@Tag(name = "2- Users", description = "Operations related to users") @RestController @RequestMapping("/api/users") public class UserController { @@ -43,6 +46,7 @@ public class UserController { @Autowired private AlbumService albumService; + @Operation(description = "Get logged in user profile", summary = "Get logged in user profile") @GetMapping("/me") @PreAuthorize("hasRole('USER')") public ResponseEntity getCurrentUser(@CurrentUser UserPrincipal currentUser) { @@ -51,6 +55,7 @@ public ResponseEntity getCurrentUser(@CurrentUser UserPrincipal cur return new ResponseEntity< >(userSummary, HttpStatus.OK); } + @Operation(description = "Check if username is available to register", summary = "Check username availability") @GetMapping("/checkUsernameAvailability") public ResponseEntity checkUsernameAvailability(@RequestParam(value = "username") String username) { UserIdentityAvailability userIdentityAvailability = userService.checkUsernameAvailability(username); @@ -58,12 +63,14 @@ public ResponseEntity checkUsernameAvailability(@Reque return new ResponseEntity< >(userIdentityAvailability, HttpStatus.OK); } + @Operation(description = "Check if email is available to register", summary = "Check email availability") @GetMapping("/checkEmailAvailability") public ResponseEntity checkEmailAvailability(@RequestParam(value = "email") String email) { UserIdentityAvailability userIdentityAvailability = userService.checkEmailAvailability(email); return new ResponseEntity< >(userIdentityAvailability, HttpStatus.OK); } + @Operation(description = "Get user profile by username", summary = "Get user profile") @GetMapping("/{username}/profile") public ResponseEntity getUSerProfile(@PathVariable(value = "username") String username) { UserProfile userProfile = userService.getUserProfile(username); @@ -71,6 +78,7 @@ public ResponseEntity getUSerProfile(@PathVariable(value = "usernam return new ResponseEntity< >(userProfile, HttpStatus.OK); } + @Operation(description = "Get posts created by user", summary = "Get posts") @GetMapping("/{username}/posts") public ResponseEntity> getPostsCreatedBy(@PathVariable(value = "username") String username, @RequestParam(value = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, @@ -80,6 +88,7 @@ public ResponseEntity> getPostsCreatedBy(@PathVariable(value return new ResponseEntity< >(response, HttpStatus.OK); } + @Operation(description = "Get albums created by user", summary = "Get albums") @GetMapping("/{username}/albums") public ResponseEntity> getUserAlbums(@PathVariable(name = "username") String username, @RequestParam(name = "page", required = false, defaultValue = AppConstants.DEFAULT_PAGE_NUMBER) Integer page, @@ -90,6 +99,7 @@ public ResponseEntity> getUserAlbums(@PathVariable(name = " return new ResponseEntity< >(response, HttpStatus.OK); } + @Operation(description = "Add user (Only for admins)", summary = "Create user") @PostMapping @PreAuthorize("hasRole('ADMIN')") public ResponseEntity addUser(@Valid @RequestBody User user) { @@ -98,6 +108,7 @@ public ResponseEntity addUser(@Valid @RequestBody User user) { return new ResponseEntity< >(newUser, HttpStatus.CREATED); } + @Operation(description = "Update user (If profile belongs to logged in user or logged in user is admin)", summary = "Update user") @PutMapping("/{username}") @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") public ResponseEntity updateUser(@Valid @RequestBody User newUser, @@ -107,6 +118,7 @@ public ResponseEntity updateUser(@Valid @RequestBody User newUser, return new ResponseEntity< >(updatedUSer, HttpStatus.CREATED); } + @Operation(description = "Delete user (For logged in user or admin)", summary = "Delete user") @DeleteMapping("/{username}") @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") public ResponseEntity deleteUser(@PathVariable(value = "username") String username, @@ -116,6 +128,7 @@ public ResponseEntity deleteUser(@PathVariable(value = "username") return new ResponseEntity< >(apiResponse, HttpStatus.OK); } + @Operation(description = "Give admin role to user (only for admins)", summary = "Promote to admin") @PutMapping("/{username}/giveAdmin") @PreAuthorize("hasRole('ADMIN')") public ResponseEntity giveAdmin(@PathVariable(name = "username") String username) { @@ -124,6 +137,7 @@ public ResponseEntity giveAdmin(@PathVariable(name = "username") St return new ResponseEntity< >(apiResponse, HttpStatus.OK); } + @Operation(description = "Take admin role from user (only for admins)", summary = "Demote to admin") @PutMapping("/{username}/takeAdmin") @PreAuthorize("hasRole('ADMIN')") public ResponseEntity takeAdmin(@PathVariable(name = "username") String username) { @@ -132,6 +146,7 @@ public ResponseEntity takeAdmin(@PathVariable(name = "username") St return new ResponseEntity< >(apiResponse, HttpStatus.OK); } + @Operation(description = "Update user profile (If profile belongs to logged in user or logged in user is admin)", summary = "Update user") @PutMapping("/setOrUpdateInfo") @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") public ResponseEntity setAddress(@CurrentUser UserPrincipal currentUser, diff --git a/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationFilter.java b/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationFilter.java index 3c162554..70876acb 100644 --- a/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationFilter.java +++ b/src/main/java/com/sopromadze/blogapi/security/JwtAuthenticationFilter.java @@ -18,40 +18,46 @@ import java.io.IOException; public class JwtAuthenticationFilter extends OncePerRequestFilter { - private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationFilter.class); - @Autowired - private JwtTokenProvider tokenProvider; - @Autowired - private CustomUserDetailsService customUserDetailsService; - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - try { - String jwt = getJwtFromRequest(request); - - if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) { - Long userId = tokenProvider.getUserIdFromJWT(jwt); - - UserDetails userDetails = customUserDetailsService.loadUserById(userId); - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, - userDetails.getAuthorities()); - authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - - SecurityContextHolder.getContext().setAuthentication(authenticationToken); - } - } catch (Exception ex) { - LOGGER.error("Could not set user authentication in security context", ex); - } - - filterChain.doFilter(request, response); - } - - private String getJwtFromRequest(HttpServletRequest request) { - String bearerToken = request.getHeader("Authorization"); - if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { - return bearerToken.substring(7, bearerToken.length()); - } - return null; - } + private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationFilter.class); + @Autowired + private JwtTokenProvider tokenProvider; + @Autowired + private CustomUserDetailsService customUserDetailsService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + try { + String jwt = getJwtFromRequest(request); + + if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) { + Long userId = tokenProvider.getUserIdFromJWT(jwt); + + UserDetails userDetails = customUserDetailsService.loadUserById(userId); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, + userDetails.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } + } catch (Exception ex) { + LOGGER.error("Could not set user authentication in security context", ex); + } + + filterChain.doFilter(request, response); + } + + private String getJwtFromRequest(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7, bearerToken.length()); + } + return null; + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + String servletPath = request.getServletPath(); + return servletPath.contains("/swagger-ui"); + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ef6d7e3d..626a1546 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -21,3 +21,8 @@ app: cors: allowedOrings: '*' + +#Swagger-Ui ~Ordering the controllers +springdoc: + swagger-ui: + tagsSorter: alpha