From 3f84fc803ec678188f26aeb56faea291ef85a47f Mon Sep 17 00:00:00 2001 From: alexZ7000 <78627928+alexZ7000@users.noreply.github.com> Date: Thu, 10 Oct 2024 17:31:14 -0300 Subject: [PATCH] project finished --- pom.xml | 5 ++ .../core/controller/CategoryController.java | 26 ++++--- .../core/controller/OrderController.java | 54 ++++++++++++++ .../core/controller/ProductController.java | 31 ++++---- .../core/controller/UserController.java | 27 +++++-- .../example/comerce/core/dto/AddressDTO.java | 8 +- .../example/comerce/core/dto/CategoryDTO.java | 8 +- .../example/comerce/core/dto/OrderDTO.java | 8 +- .../example/comerce/core/dto/ProductDTO.java | 8 +- .../com/example/comerce/core/dto/UserDTO.java | 11 ++- .../comerce/core/entities/Address.java | 2 +- .../comerce/core/entities/Category.java | 2 +- .../comerce/core/entities/LoginRequest.java | 11 +++ .../comerce/core/entities/LoginResponse.java | 18 +++++ .../example/comerce/core/entities/Order.java | 2 +- .../comerce/core/entities/OrderItem.java | 2 +- .../comerce/core/entities/Product.java | 2 +- .../core/entities/ProductCategoryId.java | 2 +- .../example/comerce/core/entities/User.java | 5 +- .../core/repository/OrderRepository.java | 12 +++ .../core/services/CategoryService.java | 17 +++-- .../comerce/core/services/OrderService.java | 45 ++++++++++++ .../comerce/core/services/ProductService.java | 21 ++++-- .../comerce/core/services/UserService.java | 48 ++++++++++-- .../shared/config/EnvConfigListener.java | 2 +- .../helpers/errors/ControllerErrors.java | 6 +- .../shared/helpers/errors/EntityErrors.java | 4 +- .../shared/helpers/errors/ServiceErrors.java | 8 +- .../comerce/shared/helpers/exceptions/.keep | 0 .../ExternalInterface.java | 3 +- .../external_interfaces/HttpModels.java | 6 +- .../validators/entities/UUIDValidator.java | 2 +- .../validators/enums/EnumValidator.java | 2 +- .../validators/errors/ValidationUtil.java | 4 +- .../security/CustomUserDetailsService.java | 30 +++++++- .../security/JWTAuthenticationFilter.java | 56 +++++++++++++- .../security/JWTAuthenticationService.java | 36 +++++++++ .../comerce/shared/security/JWTService.java | 73 ++++++++++++++++++- .../shared/security/SecurityConfig.java | 44 +++++++++-- 39 files changed, 541 insertions(+), 110 deletions(-) create mode 100644 src/main/java/com/example/comerce/core/controller/OrderController.java create mode 100644 src/main/java/com/example/comerce/core/entities/LoginRequest.java create mode 100644 src/main/java/com/example/comerce/core/entities/LoginResponse.java create mode 100644 src/main/java/com/example/comerce/core/repository/OrderRepository.java create mode 100644 src/main/java/com/example/comerce/core/services/OrderService.java delete mode 100644 src/main/java/com/example/comerce/shared/helpers/exceptions/.keep create mode 100644 src/main/java/com/example/comerce/shared/security/JWTAuthenticationService.java diff --git a/pom.xml b/pom.xml index efbca5a..65727b0 100644 --- a/pom.xml +++ b/pom.xml @@ -53,6 +53,11 @@ runtime true + + io.jsonwebtoken + jjwt + 0.9.1 + org.postgresql postgresql diff --git a/src/main/java/com/example/comerce/core/controller/CategoryController.java b/src/main/java/com/example/comerce/core/controller/CategoryController.java index a930578..8e4f447 100644 --- a/src/main/java/com/example/comerce/core/controller/CategoryController.java +++ b/src/main/java/com/example/comerce/core/controller/CategoryController.java @@ -2,7 +2,6 @@ import com.example.comerce.core.dto.CategoryDTO; import com.example.comerce.core.entities.Category; -import com.example.comerce.core.entities.Product; import com.example.comerce.core.services.CategoryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; @@ -14,37 +13,42 @@ @RestController @RequestMapping("/api/category") -public class CategoryController { +public final class CategoryController { + + private final CategoryService categoryService; + @Autowired - private CategoryService categoryService; + public CategoryController(final CategoryService categoryService) { + this.categoryService = categoryService; + } @GetMapping public ResponseEntity> getAllCategories() { - List categories = categoryService.findAll(); + final List categories = categoryService.findAll(); return ResponseEntity.ok(categories); } @GetMapping("/{id}") - public ResponseEntity getCategoryById(@PathVariable UUID id) { - Optional category = Optional.ofNullable(categoryService.findById(id)); + public ResponseEntity getCategoryById(@PathVariable final UUID id) { + final Optional category = Optional.ofNullable(categoryService.findById(id)); return category.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); } @PostMapping - public ResponseEntity createCategory(@RequestBody CategoryDTO categoryDTO) { - Category savedCategory = categoryService.save(categoryDTO.toEntity()); + public ResponseEntity createCategory(@RequestBody final CategoryDTO categoryDTO) { + final Category savedCategory = categoryService.save(categoryDTO.toEntity()); return ResponseEntity.ok(savedCategory); } @DeleteMapping("/{id}") - public ResponseEntity deleteCategory(@PathVariable UUID id) { + public ResponseEntity deleteCategory(@PathVariable final UUID id) { categoryService.delete(id); return ResponseEntity.noContent().build(); } @PutMapping("/{id}") - public ResponseEntity updateCategory(@PathVariable UUID id, @RequestBody CategoryDTO categoryDTO) { - Category updatedCategory = categoryService.update(id, categoryDTO.toEntity()); + public ResponseEntity updateCategory(@PathVariable final UUID id, @RequestBody final CategoryDTO categoryDTO) { + final Category updatedCategory = categoryService.update(id, categoryDTO.toEntity()); return ResponseEntity.ok(updatedCategory); } } diff --git a/src/main/java/com/example/comerce/core/controller/OrderController.java b/src/main/java/com/example/comerce/core/controller/OrderController.java new file mode 100644 index 0000000..e699a8c --- /dev/null +++ b/src/main/java/com/example/comerce/core/controller/OrderController.java @@ -0,0 +1,54 @@ +package com.example.comerce.core.controller; + +import com.example.comerce.core.dto.OrderDTO; +import com.example.comerce.core.entities.Order; +import com.example.comerce.core.services.OrderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@RestController +@RequestMapping("/api/orders") +public final class OrderController { + + private final OrderService orderService; + + @Autowired + public OrderController(final OrderService orderService) { + this.orderService = orderService; + } + + @GetMapping + public ResponseEntity> getAllOrders() { + final List orders = orderService.findAll(); + return ResponseEntity.ok(orders); + } + + @GetMapping("/{id}") + public ResponseEntity getOrderById(@PathVariable final UUID id) { + final Optional order = orderService.findById(id); + return order.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); + } + + @PostMapping + public ResponseEntity createOrder(@RequestBody final OrderDTO orderDTO) { + final Order savedOrder = orderService.save(orderDTO); + return ResponseEntity.ok(savedOrder); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteOrder(@PathVariable final UUID id) { + orderService.delete(id); + return ResponseEntity.noContent().build(); + } + + @PutMapping("/{id}") + public ResponseEntity updateOrder(@PathVariable final UUID id, @RequestBody final OrderDTO orderDTO) { + final Order updatedOrder = orderService.update(id, orderDTO); + return ResponseEntity.ok(updatedOrder); + } +} diff --git a/src/main/java/com/example/comerce/core/controller/ProductController.java b/src/main/java/com/example/comerce/core/controller/ProductController.java index bf4b5ac..04a6397 100644 --- a/src/main/java/com/example/comerce/core/controller/ProductController.java +++ b/src/main/java/com/example/comerce/core/controller/ProductController.java @@ -13,43 +13,42 @@ @RestController @RequestMapping("/api/products") -public class ProductController { +public final class ProductController { + + private final ProductService productService; + @Autowired - private ProductService productService; + public ProductController(final ProductService productService) { + this.productService = productService; + } @GetMapping public ResponseEntity> getAllProducts() { - List products = productService.findAll(); + final List products = productService.findAll(); return ResponseEntity.ok(products); } @GetMapping("/{id}") - public ResponseEntity getProductById(@PathVariable UUID id) { - Optional product = productService.findById(id); - return product.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); - } - - @GetMapping("/products/{category}/{id}") - public ResponseEntity getProductByCategoryAndId(@PathVariable String category, @PathVariable UUID id) { - Optional product = productService.findById(id); + public ResponseEntity getProductById(@PathVariable final UUID id) { + final Optional product = productService.findById(id); return product.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); } @PostMapping - public ResponseEntity createProduct(@RequestBody ProductDTO productDTO) { - Product savedProduct = productService.save(productDTO); + public ResponseEntity createProduct(@RequestBody final ProductDTO productDTO) { + final Product savedProduct = productService.save(productDTO); return ResponseEntity.ok(savedProduct); } @DeleteMapping("/{id}") - public ResponseEntity deleteProduct(@PathVariable UUID id) { + public ResponseEntity deleteProduct(@PathVariable final UUID id) { productService.delete(id); return ResponseEntity.noContent().build(); } @PutMapping("/{id}") - public ResponseEntity updateProduct(@PathVariable UUID id, @RequestBody ProductDTO productDTO) { - Product updatedProduct = productService.update(id, productDTO); + public ResponseEntity updateProduct(@PathVariable final UUID id, @RequestBody final ProductDTO productDTO) { + final Product updatedProduct = productService.update(id, productDTO); return ResponseEntity.ok(updatedProduct); } } diff --git a/src/main/java/com/example/comerce/core/controller/UserController.java b/src/main/java/com/example/comerce/core/controller/UserController.java index c7501b2..d8c7dbf 100644 --- a/src/main/java/com/example/comerce/core/controller/UserController.java +++ b/src/main/java/com/example/comerce/core/controller/UserController.java @@ -1,6 +1,8 @@ package com.example.comerce.core.controller; import com.example.comerce.core.dto.UserDTO; +import com.example.comerce.core.entities.LoginRequest; +import com.example.comerce.core.entities.LoginResponse; import com.example.comerce.core.entities.User; import com.example.comerce.core.services.UserService; import jakarta.validation.Valid; @@ -14,31 +16,40 @@ @RestController @RequestMapping("/api/users") -public class UserController { +public final class UserController { + + private final UserService userService; @Autowired - private UserService userService; + public UserController(final UserService userService) { + this.userService = userService; + } @GetMapping public ResponseEntity> getAllUsers() { - List users = userService.findAll(); + final List users = userService.findAll(); return ResponseEntity.ok(users); } @GetMapping("/{id}") - public ResponseEntity getUserById(@PathVariable UUID id) { - Optional user = userService.findById(id); + public ResponseEntity getUserById(@PathVariable final UUID id) { + final Optional user = userService.findById(id); return user.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); } @PostMapping - public ResponseEntity createUser(@Valid @RequestBody UserDTO userDTO) { - User savedUser = userService.save(userDTO); + public ResponseEntity createUser(@Valid @RequestBody final UserDTO userDTO) { + final User savedUser = userService.save(userDTO); return ResponseEntity.ok(savedUser); } + @PostMapping("/login") + public LoginResponse login(@RequestBody final LoginRequest request) throws Exception { + return userService.login(request.getEmail(), request.getPassword()); + } + @DeleteMapping("/{id}") - public ResponseEntity deleteUser(@PathVariable UUID id) { + public ResponseEntity deleteUser(@PathVariable final UUID id) { userService.delete(id); return ResponseEntity.noContent().build(); } diff --git a/src/main/java/com/example/comerce/core/dto/AddressDTO.java b/src/main/java/com/example/comerce/core/dto/AddressDTO.java index aabbb4d..c911ef8 100644 --- a/src/main/java/com/example/comerce/core/dto/AddressDTO.java +++ b/src/main/java/com/example/comerce/core/dto/AddressDTO.java @@ -7,7 +7,7 @@ @Getter @Setter -public class AddressDTO { +public final class AddressDTO { @NotBlank(message = "CEP não pode estar em branco") private String postal_code; @@ -30,7 +30,7 @@ public class AddressDTO { private String state; public Address toEntity() { - Address address = new Address(); + final Address address = new Address(); address.setPostal_code(this.postal_code); address.setStreet(this.street); address.setNumber(this.number); @@ -41,8 +41,8 @@ public Address toEntity() { return address; } - public static AddressDTO toDTO(Address address) { - AddressDTO addressDTO = new AddressDTO(); + public static AddressDTO toDTO(final Address address) { + final AddressDTO addressDTO = new AddressDTO(); addressDTO.setPostal_code(address.getPostal_code()); addressDTO.setStreet(address.getStreet()); addressDTO.setNumber(address.getNumber()); diff --git a/src/main/java/com/example/comerce/core/dto/CategoryDTO.java b/src/main/java/com/example/comerce/core/dto/CategoryDTO.java index 93fbc50..0975d86 100644 --- a/src/main/java/com/example/comerce/core/dto/CategoryDTO.java +++ b/src/main/java/com/example/comerce/core/dto/CategoryDTO.java @@ -7,19 +7,19 @@ @Getter @Setter -public class CategoryDTO { +public final class CategoryDTO { @Size(min = 10, max = 255, message = "A descrição da categoria deve ter entre 10 e 255 caracteres") private String description; public Category toEntity() { - Category category = new Category(); + final Category category = new Category(); category.setDescription(this.description); return category; } - public static CategoryDTO toDTO(CategoryDTO category) { - CategoryDTO categoryDTO = new CategoryDTO(); + public static CategoryDTO toDTO(final CategoryDTO category) { + final CategoryDTO categoryDTO = new CategoryDTO(); categoryDTO.setDescription(category.getDescription()); return categoryDTO; } diff --git a/src/main/java/com/example/comerce/core/dto/OrderDTO.java b/src/main/java/com/example/comerce/core/dto/OrderDTO.java index 30a3d6c..2935218 100644 --- a/src/main/java/com/example/comerce/core/dto/OrderDTO.java +++ b/src/main/java/com/example/comerce/core/dto/OrderDTO.java @@ -11,7 +11,7 @@ @Getter @Setter -public class OrderDTO { +public final class OrderDTO { @NotNull(message = "Data do pedido não pode ser null") @FutureOrPresent(message = "Data do pedido deve ser no presente ou no futuro") @@ -24,15 +24,15 @@ public class OrderDTO { private double total_price; public Order toEntity() { - Order order = new Order(); + final Order order = new Order(); order.setDate(this.date); order.setDiscount(this.discount); order.setDiscount(this.total_price); return order; } - public static OrderDTO toDTO(OrderDTO order) { - OrderDTO orderDTO = new OrderDTO(); + public static OrderDTO toDTO(final OrderDTO order) { + final OrderDTO orderDTO = new OrderDTO(); orderDTO.setDate(order.getDate()); orderDTO.setDiscount(order.getDiscount()); orderDTO.setTotal_price(order.getDiscount()); diff --git a/src/main/java/com/example/comerce/core/dto/ProductDTO.java b/src/main/java/com/example/comerce/core/dto/ProductDTO.java index d98bf03..426595f 100644 --- a/src/main/java/com/example/comerce/core/dto/ProductDTO.java +++ b/src/main/java/com/example/comerce/core/dto/ProductDTO.java @@ -9,7 +9,7 @@ @Getter @Setter -public class ProductDTO { +public final class ProductDTO { @NotBlank(message = "O nome do produto não pode estar em branco") private String name; @@ -30,7 +30,7 @@ public class ProductDTO { private double price; public Product toEntity() { - Product product = new Product(); + final Product product = new Product(); product.setName(this.name); product.setStock_quantity(this.stock_quantity); product.setCost_price(this.cost_price); @@ -40,8 +40,8 @@ public Product toEntity() { return product; } - public static ProductDTO toDTO(ProductDTO product) { - ProductDTO productDTO = new ProductDTO(); + public static ProductDTO toDTO(final ProductDTO product) { + final ProductDTO productDTO = new ProductDTO(); productDTO.setName(product.getName()); productDTO.setStock_quantity(product.getStock_quantity()); productDTO.setCost_price(product.getCost_price()); diff --git a/src/main/java/com/example/comerce/core/dto/UserDTO.java b/src/main/java/com/example/comerce/core/dto/UserDTO.java index 6c2a169..ef35df0 100644 --- a/src/main/java/com/example/comerce/core/dto/UserDTO.java +++ b/src/main/java/com/example/comerce/core/dto/UserDTO.java @@ -10,7 +10,7 @@ @Getter @Setter -public class UserDTO { +public final class UserDTO { @NotBlank(message = "Nome não pode estar em branco") @Size(min = 3, max = 100, message = "Nome deve ter no mínimo 3 e no máximo 100 caracteres") private String name; @@ -27,21 +27,26 @@ public class UserDTO { @Email(message = "E-mail inválido") private String email; + @NotBlank(message = "Senha não pode estar em branco") + @Size(min = 6, max = 255, message = "Senha deve ter no mínimo 6 e no máximo 255 caracteres") + private String password; + @NotNull(message = "Endereço não pode ser null") private AddressDTO address; public User toEntity() { - User user = new User(); + final User user = new User(); user.setName(this.name); user.setTelephone(this.telephone); user.setCpf(this.cpf); user.setEmail(this.email); + user.setPassword(this.password); user.setAddress(this.address.toEntity()); return user; } public static UserDTO toDTO(User user) { - UserDTO userDTO = new UserDTO(); + final UserDTO userDTO = new UserDTO(); userDTO.setName(user.getName()); userDTO.setTelephone(user.getTelephone()); userDTO.setCpf(user.getCpf()); diff --git a/src/main/java/com/example/comerce/core/entities/Address.java b/src/main/java/com/example/comerce/core/entities/Address.java index c056921..77d189f 100644 --- a/src/main/java/com/example/comerce/core/entities/Address.java +++ b/src/main/java/com/example/comerce/core/entities/Address.java @@ -10,7 +10,7 @@ @Table(name = "address") @Getter @Setter -public class Address { +public final class Address { @Id @GeneratedValue(strategy = GenerationType.AUTO) diff --git a/src/main/java/com/example/comerce/core/entities/Category.java b/src/main/java/com/example/comerce/core/entities/Category.java index d24ae1a..fcdd686 100644 --- a/src/main/java/com/example/comerce/core/entities/Category.java +++ b/src/main/java/com/example/comerce/core/entities/Category.java @@ -11,7 +11,7 @@ @Table(name = "category") @Getter @Setter -public class Category { +public final class Category { @Id @GeneratedValue(strategy = GenerationType.AUTO) diff --git a/src/main/java/com/example/comerce/core/entities/LoginRequest.java b/src/main/java/com/example/comerce/core/entities/LoginRequest.java new file mode 100644 index 0000000..a109863 --- /dev/null +++ b/src/main/java/com/example/comerce/core/entities/LoginRequest.java @@ -0,0 +1,11 @@ +package com.example.comerce.core.entities; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public final class LoginRequest { + private String email; + private String password; +} diff --git a/src/main/java/com/example/comerce/core/entities/LoginResponse.java b/src/main/java/com/example/comerce/core/entities/LoginResponse.java new file mode 100644 index 0000000..d7f39b0 --- /dev/null +++ b/src/main/java/com/example/comerce/core/entities/LoginResponse.java @@ -0,0 +1,18 @@ +package com.example.comerce.core.entities; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class LoginResponse { + private String token; + private User user; + + public LoginResponse() {} + + public LoginResponse(final String token, final User user) { + this.token = token; + this.user = user; + } +} diff --git a/src/main/java/com/example/comerce/core/entities/Order.java b/src/main/java/com/example/comerce/core/entities/Order.java index bedee9e..1a30e2d 100644 --- a/src/main/java/com/example/comerce/core/entities/Order.java +++ b/src/main/java/com/example/comerce/core/entities/Order.java @@ -12,7 +12,7 @@ @Table(name = "orders") @Getter @Setter -public class Order { +public final class Order { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "order_id", nullable = false, unique = true, updatable = false, length = 36) diff --git a/src/main/java/com/example/comerce/core/entities/OrderItem.java b/src/main/java/com/example/comerce/core/entities/OrderItem.java index e7a6e0e..ecd6f99 100644 --- a/src/main/java/com/example/comerce/core/entities/OrderItem.java +++ b/src/main/java/com/example/comerce/core/entities/OrderItem.java @@ -12,7 +12,7 @@ @Table(name = "order_items") @Getter @Setter -public class OrderItem { +public final class OrderItem { @Id @GeneratedValue(strategy = GenerationType.AUTO) diff --git a/src/main/java/com/example/comerce/core/entities/Product.java b/src/main/java/com/example/comerce/core/entities/Product.java index 44b5df6..126b3b4 100644 --- a/src/main/java/com/example/comerce/core/entities/Product.java +++ b/src/main/java/com/example/comerce/core/entities/Product.java @@ -15,7 +15,7 @@ @Table(name = "product") @Getter @Setter -public class Product { +public final class Product { @Id @GeneratedValue(strategy = GenerationType.AUTO) diff --git a/src/main/java/com/example/comerce/core/entities/ProductCategoryId.java b/src/main/java/com/example/comerce/core/entities/ProductCategoryId.java index 479c68a..620b552 100644 --- a/src/main/java/com/example/comerce/core/entities/ProductCategoryId.java +++ b/src/main/java/com/example/comerce/core/entities/ProductCategoryId.java @@ -12,7 +12,7 @@ @Getter @Setter @EqualsAndHashCode -public class ProductCategoryId { +public final class ProductCategoryId { @Column(insertable = false, updatable = false) private UUID product_id; diff --git a/src/main/java/com/example/comerce/core/entities/User.java b/src/main/java/com/example/comerce/core/entities/User.java index d5f5e51..be36b10 100644 --- a/src/main/java/com/example/comerce/core/entities/User.java +++ b/src/main/java/com/example/comerce/core/entities/User.java @@ -12,7 +12,7 @@ @Table(name = "users") @Getter @Setter -public class User { +public final class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) @@ -31,6 +31,9 @@ public class User { @Column(length = 100, nullable = false, unique = true) private String email; + @Column(nullable = false) + private String password; + @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "address_id", referencedColumnName = "address_id") private Address address; diff --git a/src/main/java/com/example/comerce/core/repository/OrderRepository.java b/src/main/java/com/example/comerce/core/repository/OrderRepository.java new file mode 100644 index 0000000..2a6309f --- /dev/null +++ b/src/main/java/com/example/comerce/core/repository/OrderRepository.java @@ -0,0 +1,12 @@ +package com.example.comerce.core.repository; + +import com.example.comerce.core.entities.Order; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface OrderRepository extends JpaRepository { + +} diff --git a/src/main/java/com/example/comerce/core/services/CategoryService.java b/src/main/java/com/example/comerce/core/services/CategoryService.java index d50a187..8841d0e 100644 --- a/src/main/java/com/example/comerce/core/services/CategoryService.java +++ b/src/main/java/com/example/comerce/core/services/CategoryService.java @@ -9,28 +9,33 @@ import java.util.UUID; @Service -public class CategoryService { +public final class CategoryService { + + private final CategoryRepository categoryRepository; + @Autowired - private CategoryRepository categoryRepository; + public CategoryService(final CategoryRepository categoryRepository) { + this.categoryRepository = categoryRepository; + } public List findAll() { return categoryRepository.findAll(); } - public Category save(Category category) { + public Category save(final Category category) { return categoryRepository.save(category); } - public void delete(UUID categoryId) { + public void delete(final UUID categoryId) { categoryRepository.deleteById(categoryId); } - public Category update(UUID categoryId, Category category) { + public Category update(final UUID categoryId, final Category category) { category.setCategory_id(categoryId); return categoryRepository.save(category); } - public Category findById(UUID categoryId) { + public Category findById(final UUID categoryId) { return categoryRepository.findById(categoryId).orElse(null); } } diff --git a/src/main/java/com/example/comerce/core/services/OrderService.java b/src/main/java/com/example/comerce/core/services/OrderService.java new file mode 100644 index 0000000..658e8b3 --- /dev/null +++ b/src/main/java/com/example/comerce/core/services/OrderService.java @@ -0,0 +1,45 @@ +package com.example.comerce.core.services; + +import com.example.comerce.core.dto.OrderDTO; +import com.example.comerce.core.entities.Order; +import com.example.comerce.core.repository.OrderRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Service +public final class OrderService { + + private final OrderRepository orderRepository; + + @Autowired + public OrderService(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } + + public List findAll() { + return orderRepository.findAll(); + } + + public Optional findById(final UUID orderId) { + return orderRepository.findById(orderId); + } + + public Order save(final OrderDTO orderDTO) { + final Order order = orderDTO.toEntity(); + return orderRepository.save(order); + } + + public void delete(final UUID orderId) { + orderRepository.deleteById(orderId); + } + + public Order update(final UUID orderId, final OrderDTO orderDTO) { + final Order order = orderDTO.toEntity(); + order.setOrder_id(orderId); + return orderRepository.save(order); + } +} diff --git a/src/main/java/com/example/comerce/core/services/ProductService.java b/src/main/java/com/example/comerce/core/services/ProductService.java index e5558e4..91aca71 100644 --- a/src/main/java/com/example/comerce/core/services/ProductService.java +++ b/src/main/java/com/example/comerce/core/services/ProductService.java @@ -11,29 +11,34 @@ import java.util.UUID; @Service -public class ProductService { +public final class ProductService { + + private final ProductRepository productRepository; + @Autowired - private ProductRepository productRepository; + public ProductService(ProductRepository productRepository) { + this.productRepository = productRepository; + } public List findAll() { return productRepository.findAll(); } - public Optional findById(UUID productId) { + public Optional findById(final UUID productId) { return productRepository.findById(productId); } - public Product save(ProductDTO productDTO) { - Product product = productDTO.toEntity(); + public Product save(final ProductDTO productDTO) { + final Product product = productDTO.toEntity(); return productRepository.save(product); } - public void delete(UUID productId) { + public void delete(final UUID productId) { productRepository.deleteById(productId); } - public Product update(UUID productId, ProductDTO productDTO) { - Product product = productDTO.toEntity(); + public Product update(final UUID productId, final ProductDTO productDTO) { + final Product product = productDTO.toEntity(); product.setProduct_id(productId); return productRepository.save(product); } diff --git a/src/main/java/com/example/comerce/core/services/UserService.java b/src/main/java/com/example/comerce/core/services/UserService.java index dc49886..9b20f94 100644 --- a/src/main/java/com/example/comerce/core/services/UserService.java +++ b/src/main/java/com/example/comerce/core/services/UserService.java @@ -1,9 +1,16 @@ package com.example.comerce.core.services; import com.example.comerce.core.dto.UserDTO; +import com.example.comerce.core.entities.LoginResponse; import com.example.comerce.core.entities.User; import com.example.comerce.core.repository.UserRepository; +import com.example.comerce.shared.security.JWTService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import java.util.List; @@ -11,24 +18,53 @@ import java.util.UUID; @Service -public class UserService { +public final class UserService { + + private final UserRepository userRepository; + private final JWTService jwtService; + private final PasswordEncoder passwordEncoder; + private final AuthenticationManager authenticationManager; + @Autowired - private UserRepository userRepository; + public UserService(final UserRepository userRepository, + final JWTService jwtService, + final PasswordEncoder passwordEncoder, + final AuthenticationManager authenticationManager) { + this.userRepository = userRepository; + this.jwtService = jwtService; + this.passwordEncoder = passwordEncoder; + this.authenticationManager = authenticationManager; + } public List findAll() { return userRepository.findAll(); } - public Optional findById(UUID userId) { + public Optional findById(final UUID userId) { return userRepository.findById(userId); } - public User save(UserDTO userDTO) { - User user = userDTO.toEntity(); + public User save(final UserDTO userDTO) { + final User user = userDTO.toEntity(); + final String password = passwordEncoder.encode(user.getPassword()); + user.setPassword(password); return userRepository.save(user); } - public void delete(UUID userId) { + public void delete(final UUID userId) { userRepository.deleteById(userId); } + + public LoginResponse login(final String email, final String senha) throws Exception { + final Authentication autenticacao = authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(email, senha)); + + SecurityContextHolder.getContext().setAuthentication(autenticacao); + final String token = "Bearer " + jwtService.generateToken(email); + + final User user = userRepository.findByEmail(email).orElseThrow(() -> new Exception("User not found")); + + return new LoginResponse(token, user); + } + } diff --git a/src/main/java/com/example/comerce/shared/config/EnvConfigListener.java b/src/main/java/com/example/comerce/shared/config/EnvConfigListener.java index 7d7a612..dadde90 100644 --- a/src/main/java/com/example/comerce/shared/config/EnvConfigListener.java +++ b/src/main/java/com/example/comerce/shared/config/EnvConfigListener.java @@ -11,7 +11,7 @@ import java.io.IOException; import java.util.Properties; -public class EnvConfigListener implements ApplicationListener { +public final class EnvConfigListener implements ApplicationListener { private static final Logger logger = LoggerFactory.getLogger(EnvConfigListener.class); diff --git a/src/main/java/com/example/comerce/shared/helpers/errors/ControllerErrors.java b/src/main/java/com/example/comerce/shared/helpers/errors/ControllerErrors.java index c3e60fa..b9d62b8 100644 --- a/src/main/java/com/example/comerce/shared/helpers/errors/ControllerErrors.java +++ b/src/main/java/com/example/comerce/shared/helpers/errors/ControllerErrors.java @@ -1,14 +1,14 @@ package com.example.comerce.shared.helpers.errors; -public class ControllerErrors { - public static class MissingParameters extends BaseError { +public final class ControllerErrors { + public final static class MissingParameters extends BaseError { public MissingParameters(final String fieldName) { super("Field " + fieldName + " is missing"); } } - public static class WrongTypeParameters extends BaseError { + public final static class WrongTypeParameters extends BaseError { public WrongTypeParameters(final String fieldName, final String fieldTypeExpected, final String fieldTypeReceived) { super("Field " + fieldName + " isn't in the right type.\n Received: " + fieldTypeReceived + ".\n Expected: " + fieldTypeExpected + "."); } diff --git a/src/main/java/com/example/comerce/shared/helpers/errors/EntityErrors.java b/src/main/java/com/example/comerce/shared/helpers/errors/EntityErrors.java index 9abf505..bebc3df 100644 --- a/src/main/java/com/example/comerce/shared/helpers/errors/EntityErrors.java +++ b/src/main/java/com/example/comerce/shared/helpers/errors/EntityErrors.java @@ -12,13 +12,13 @@ public EntityError(final String message){ } } - public static class EntityParameterTypeError extends EntityError { + public final static class EntityParameterTypeError extends EntityError { public EntityParameterTypeError(final String message) { super(message); } } - public static class EntityParameterError extends EntityError { + public final static class EntityParameterError extends EntityError { public EntityParameterError(final String message) { super(message); } diff --git a/src/main/java/com/example/comerce/shared/helpers/errors/ServiceErrors.java b/src/main/java/com/example/comerce/shared/helpers/errors/ServiceErrors.java index e0c461a..eebfc57 100644 --- a/src/main/java/com/example/comerce/shared/helpers/errors/ServiceErrors.java +++ b/src/main/java/com/example/comerce/shared/helpers/errors/ServiceErrors.java @@ -1,19 +1,19 @@ package com.example.comerce.shared.helpers.errors; -public class ServiceErrors { - public static class NoItemsFound extends BaseError { +public final class ServiceErrors { + public final static class NoItemsFound extends BaseError { public NoItemsFound(final String message) { super("No items found for " + message); } } - public static class DuplicatedItem extends BaseError { + public final static class DuplicatedItem extends BaseError { public DuplicatedItem(final String message) { super("The item already exists for this " + message); } } - public static class ForbiddenAction extends BaseError { + public final static class ForbiddenAction extends BaseError { public ForbiddenAction(final String message) { super("The action is forbidden for this " + message); } diff --git a/src/main/java/com/example/comerce/shared/helpers/exceptions/.keep b/src/main/java/com/example/comerce/shared/helpers/exceptions/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/example/comerce/shared/helpers/external_interfaces/ExternalInterface.java b/src/main/java/com/example/comerce/shared/helpers/external_interfaces/ExternalInterface.java index babbb05..4d53e3e 100644 --- a/src/main/java/com/example/comerce/shared/helpers/external_interfaces/ExternalInterface.java +++ b/src/main/java/com/example/comerce/shared/helpers/external_interfaces/ExternalInterface.java @@ -2,7 +2,7 @@ import java.util.Map; -class ExternalInterface { +final class ExternalInterface { protected interface IRequest { Map getData(); } @@ -11,5 +11,4 @@ protected interface IResponse { int getStatusCode(); Map getData(); } - } diff --git a/src/main/java/com/example/comerce/shared/helpers/external_interfaces/HttpModels.java b/src/main/java/com/example/comerce/shared/helpers/external_interfaces/HttpModels.java index c0e95ae..9d10849 100644 --- a/src/main/java/com/example/comerce/shared/helpers/external_interfaces/HttpModels.java +++ b/src/main/java/com/example/comerce/shared/helpers/external_interfaces/HttpModels.java @@ -9,9 +9,9 @@ import java.util.Map; import java.util.Objects; -public class HttpModels { +public final class HttpModels { - public static class HttpRequest implements IRequest { + public final static class HttpRequest implements IRequest { private final Map body; private final Map headers; private final Map queryParams; @@ -60,7 +60,7 @@ public String toString() { } @Getter - public static class HttpResponse implements IResponse { + public final static class HttpResponse implements IResponse { private final HttpStatus statusCode; private final Map body; private final Map headers; diff --git a/src/main/java/com/example/comerce/shared/helpers/validators/entities/UUIDValidator.java b/src/main/java/com/example/comerce/shared/helpers/validators/entities/UUIDValidator.java index 55b6da0..584ac42 100644 --- a/src/main/java/com/example/comerce/shared/helpers/validators/entities/UUIDValidator.java +++ b/src/main/java/com/example/comerce/shared/helpers/validators/entities/UUIDValidator.java @@ -2,7 +2,7 @@ import java.util.UUID; -public class UUIDValidator { +public final class UUIDValidator { public static boolean validateUUID(final String campo, final String value) { try { if (value.length() == 36){ diff --git a/src/main/java/com/example/comerce/shared/helpers/validators/enums/EnumValidator.java b/src/main/java/com/example/comerce/shared/helpers/validators/enums/EnumValidator.java index bc8d9d7..a58a0be 100644 --- a/src/main/java/com/example/comerce/shared/helpers/validators/enums/EnumValidator.java +++ b/src/main/java/com/example/comerce/shared/helpers/validators/enums/EnumValidator.java @@ -1,6 +1,6 @@ package com.example.comerce.shared.helpers.validators.enums; -public class EnumValidator { +public final class EnumValidator { public static boolean validateEnum(Enum[] enumValues, String value) { for (Enum enumValue : enumValues) { diff --git a/src/main/java/com/example/comerce/shared/helpers/validators/errors/ValidationUtil.java b/src/main/java/com/example/comerce/shared/helpers/validators/errors/ValidationUtil.java index 7ee6e2a..c740338 100644 --- a/src/main/java/com/example/comerce/shared/helpers/validators/errors/ValidationUtil.java +++ b/src/main/java/com/example/comerce/shared/helpers/validators/errors/ValidationUtil.java @@ -7,8 +7,8 @@ import java.util.Map; import java.util.Set; -public class ValidationUtil { - public static Map validateAndGetViolations(Validator validator, T entity) { +public final class ValidationUtil { + public static Map validateAndGetViolations(final Validator validator, final T entity) { final Set> violations = validator.validate(entity); if (violations.isEmpty()) { diff --git a/src/main/java/com/example/comerce/shared/security/CustomUserDetailsService.java b/src/main/java/com/example/comerce/shared/security/CustomUserDetailsService.java index b7b107d..57ff08e 100644 --- a/src/main/java/com/example/comerce/shared/security/CustomUserDetailsService.java +++ b/src/main/java/com/example/comerce/shared/security/CustomUserDetailsService.java @@ -1,5 +1,33 @@ package com.example.comerce.shared.security; -public class CustomUserDetailsService { +import com.example.comerce.core.entities.User; +import com.example.comerce.core.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@Service +public final class CustomUserDetailsService implements UserDetailsService { + + private final UserRepository userRepository; + + @Autowired + public CustomUserDetailsService(final UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { + final User user = userRepository.findByEmail(username) + .orElseThrow(() -> new UsernameNotFoundException("User not found")); + return org.springframework.security.core.userdetails.User + .withUsername(user.getEmail()) + .password(user.getPassword()) + .roles("USER") + .build(); + } } + diff --git a/src/main/java/com/example/comerce/shared/security/JWTAuthenticationFilter.java b/src/main/java/com/example/comerce/shared/security/JWTAuthenticationFilter.java index a4fc2cb..a58ae95 100644 --- a/src/main/java/com/example/comerce/shared/security/JWTAuthenticationFilter.java +++ b/src/main/java/com/example/comerce/shared/security/JWTAuthenticationFilter.java @@ -1,5 +1,59 @@ package com.example.comerce.shared.security; -public class JWTAuthenticationFilter { +import jakarta.servlet.ServletException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +@Component +public final class JWTAuthenticationFilter extends OncePerRequestFilter { + + private final JWTService jwtService; + private CustomUserDetailsService userDetailsService = null; + + @Autowired + public JWTAuthenticationFilter(final JWTService jwtService, final CustomUserDetailsService userDetailsService) { + this.jwtService = jwtService; + this.userDetailsService = userDetailsService; + } + + @Override + protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) + throws IOException, ServletException { + String token = request.getHeader("Authorization"); + + if (token != null && token.startsWith("Bearer ")) { + token = token.substring(7); + String username = null; + + try { + username = jwtService.extractUsername(token); + } catch (Exception e) { + // Handle exception + } + + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + final UserDetails userDetails = userDetailsService.loadUserByUsername(username); + try { + if (jwtService.validateToken(token, userDetails.getUsername())) { + var authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + filterChain.doFilter(request, response); + } } diff --git a/src/main/java/com/example/comerce/shared/security/JWTAuthenticationService.java b/src/main/java/com/example/comerce/shared/security/JWTAuthenticationService.java new file mode 100644 index 0000000..1264306 --- /dev/null +++ b/src/main/java/com/example/comerce/shared/security/JWTAuthenticationService.java @@ -0,0 +1,36 @@ +package com.example.comerce.shared.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Service; + +@Service +public final class JWTAuthenticationService { + + private final JWTService jwtService; + private final CustomUserDetailsService userDetailsService; + + @Autowired + public JWTAuthenticationService(final JWTService jwtService, final CustomUserDetailsService userDetailsService) { + this.jwtService = jwtService; + this.userDetailsService = userDetailsService; + } + + public void authenticateUser(final String authorizationHeader) throws Exception { + if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { + final String token = authorizationHeader.substring(7); + final String username = jwtService.extractUsername(token); + + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + final UserDetails userDetails = userDetailsService.loadUserByUsername(username); + + if (jwtService.validateToken(token, userDetails.getUsername())) { + var authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } + } + } +} diff --git a/src/main/java/com/example/comerce/shared/security/JWTService.java b/src/main/java/com/example/comerce/shared/security/JWTService.java index 331d7eb..9ad12a7 100644 --- a/src/main/java/com/example/comerce/shared/security/JWTService.java +++ b/src/main/java/com/example/comerce/shared/security/JWTService.java @@ -1,5 +1,76 @@ package com.example.comerce.shared.security; -public class JWTService { +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Service; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Service +public final class JWTService { + + private final static String SECRET_KEY = "secret"; + + public String generateToken(final String username) throws Exception { + final Map claims = new HashMap<>(); + claims.put("username", username); + claims.put("exp", System.currentTimeMillis() + 1000 * 60 * 60); // 1 hora + return createToken(claims); + } + + private String createToken(Map claims) throws Exception { + final String header = Base64.getUrlEncoder().encodeToString("{\"alg\":\"HS256\",\"typ\":\"JWT\"}".getBytes(StandardCharsets.UTF_8)); + final String payload = Base64.getUrlEncoder().encodeToString(new ObjectMapper().writeValueAsBytes(claims)); + + final String signature = createSignature(header, payload); + + return header + "." + payload + "." + signature; + } + + private String createSignature(final String header, final String payload) throws Exception { + final String data = header + "." + payload; + final byte[] hmacSha256 = hmacSha256(data); + return Base64.getUrlEncoder().encodeToString(hmacSha256); + } + + private byte[] hmacSha256(final String data) throws Exception { + final javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA256"); + mac.init(new javax.crypto.spec.SecretKeySpec(JWTService.SECRET_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); + return mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); + } + + public String extractUsername(final String token) throws Exception { + final String payload = token.split("\\.")[1]; + final Map claims = new ObjectMapper().readValue(Base64.getUrlDecoder().decode(payload), Map.class); + return (String) claims.get("username"); + } + + public boolean validateToken(final String token, final String username) throws Exception { + final String extractedUsername = extractUsername(token); + return (extractedUsername.equals(username) && !isTokenExpired(token) && verifySignature(token)); + } + + private boolean isTokenExpired(final String token) throws Exception { + final String payload = token.split("\\.")[1]; + final Map claims = new ObjectMapper().readValue(Base64.getUrlDecoder().decode(payload), Map.class); + final long expiration = (Long) claims.get("exp"); + return new Date(expiration).before(new Date()); + } + + private boolean verifySignature(final String token) throws Exception { + final String[] parts = token.split("\\."); + if (parts.length != 3) { + return false; + } + + final String header = parts[0]; + final String payload = parts[1]; + final String signature = parts[2]; + + final String expectedSignature = createSignature(header, payload); + return signature.equals(expectedSignature); + } } diff --git a/src/main/java/com/example/comerce/shared/security/SecurityConfig.java b/src/main/java/com/example/comerce/shared/security/SecurityConfig.java index e1537ef..3f49a3e 100644 --- a/src/main/java/com/example/comerce/shared/security/SecurityConfig.java +++ b/src/main/java/com/example/comerce/shared/security/SecurityConfig.java @@ -1,25 +1,55 @@ package com.example.comerce.shared.security; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration +@EnableWebSecurity public class SecurityConfig { - @Bean + private final UserDetailsService userDetailsService; + private final JWTService jwtService; + @Autowired - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + public SecurityConfig(final JWTService jwtService, final UserDetailsService userDetailsService) { + this.jwtService = jwtService; + this.userDetailsService = userDetailsService; + } + + @Bean + public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(authorize -> authorize - .requestMatchers("/api/users").permitAll() - .anyRequest().permitAll() - ); + .requestMatchers("/api/users/login").permitAll() + .anyRequest().authenticated() + ) + .addFilterBefore(new JWTAuthenticationFilter(jwtService, (CustomUserDetailsService) userDetailsService), UsernamePasswordAuthenticationFilter.class); // Adicionando o filtro return http.build(); } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { + final AuthenticationManagerBuilder authenticationManagerBuilder = + http.getSharedObject(AuthenticationManagerBuilder.class); + authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); + return authenticationManagerBuilder.build(); + } }