diff --git a/build.gradle b/build.gradle index 1e55bfb..aa899eb 100644 --- a/build.gradle +++ b/build.gradle @@ -57,6 +57,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'io.jsonwebtoken:jjwt:0.9.1' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0' implementation 'org.apache.httpcomponents:httpclient:4.5' compileOnly 'org.projectlombok:lombok' diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/infrastructure/auth/AdminAuthorizationAspect.java b/src/main/java/br/com/fiap/grupo30/fastfood/infrastructure/auth/AdminAuthorizationAspect.java new file mode 100644 index 0000000..25e2167 --- /dev/null +++ b/src/main/java/br/com/fiap/grupo30/fastfood/infrastructure/auth/AdminAuthorizationAspect.java @@ -0,0 +1,64 @@ +package br.com.fiap.grupo30.fastfood.infrastructure.auth; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Base64; +import java.util.List; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +@Aspect +@Component +public class AdminAuthorizationAspect { + + private final ObjectMapper objectMapper = new ObjectMapper(); + private static String ADMIN_ROLE = "admin-group"; + private static String BEARER_TYPE = "Bearer"; + private static Integer JWT_PARTS = 3; + + @Before("@annotation(AdminRequired)") + public void checkAdminRole() throws Exception { + ServletRequestAttributes attrs = + (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + HttpServletRequest request = attrs.getRequest(); + HttpServletResponse response = attrs.getResponse(); + + String authorizationHeader = request.getHeader("Authorization"); + + if (authorizationHeader == null || !authorizationHeader.startsWith(BEARER_TYPE)) { + response.sendError( + HttpServletResponse.SC_UNAUTHORIZED, "Missing or invalid Authorization header"); + return; + } + + String jwtToken = authorizationHeader.substring(7); + + String[] tokenParts = jwtToken.split("\\."); + + if (tokenParts.length != JWT_PARTS) { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid JWT token structure"); + return; + } + + String payload = new String(Base64.getDecoder().decode(tokenParts[1])); + JsonNode jsonNode = objectMapper.readTree(payload); + + JsonNode groupsNode = jsonNode.get("cognito:groups"); + if (groupsNode != null && groupsNode.isArray()) { + List groups = objectMapper.convertValue(groupsNode, List.class); + if (!groups.contains(ADMIN_ROLE)) { + response.sendError( + HttpServletResponse.SC_UNAUTHORIZED, "User does not have admin role"); + return; + } + } else { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "No cognito:groups found"); + return; + } + } +} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/infrastructure/auth/AdminRequired.java b/src/main/java/br/com/fiap/grupo30/fastfood/infrastructure/auth/AdminRequired.java new file mode 100644 index 0000000..61cff40 --- /dev/null +++ b/src/main/java/br/com/fiap/grupo30/fastfood/infrastructure/auth/AdminRequired.java @@ -0,0 +1,10 @@ +package br.com.fiap.grupo30.fastfood.infrastructure.auth; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface AdminRequired {} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/presentation/controllers/OrderController.java b/src/main/java/br/com/fiap/grupo30/fastfood/presentation/controllers/OrderController.java index 685c4c2..1856c3f 100644 --- a/src/main/java/br/com/fiap/grupo30/fastfood/presentation/controllers/OrderController.java +++ b/src/main/java/br/com/fiap/grupo30/fastfood/presentation/controllers/OrderController.java @@ -1,6 +1,7 @@ package br.com.fiap.grupo30.fastfood.presentation.controllers; import br.com.fiap.grupo30.fastfood.domain.usecases.order.*; +import br.com.fiap.grupo30.fastfood.infrastructure.auth.AdminRequired; import br.com.fiap.grupo30.fastfood.presentation.presenters.dto.AddCustomerCpfRequest; import br.com.fiap.grupo30.fastfood.presentation.presenters.dto.AddOrderProductRequest; import br.com.fiap.grupo30.fastfood.presentation.presenters.dto.OrderDTO; @@ -147,6 +148,7 @@ public ResponseEntity finishPreparingOrder(@PathVariable Long orderId) return ResponseEntity.ok().body(order); } + @AdminRequired() @PostMapping(value = "/{orderId}/deliver") @Operation( summary = "Deliver an order", diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/presentation/controllers/PaymentResource.java b/src/main/java/br/com/fiap/grupo30/fastfood/presentation/controllers/PaymentResource.java index 9415fd5..246a9dd 100644 --- a/src/main/java/br/com/fiap/grupo30/fastfood/presentation/controllers/PaymentResource.java +++ b/src/main/java/br/com/fiap/grupo30/fastfood/presentation/controllers/PaymentResource.java @@ -2,6 +2,7 @@ import br.com.fiap.grupo30.fastfood.domain.usecases.payment.CollectOrderPaymentViaCashUseCase; import br.com.fiap.grupo30.fastfood.domain.usecases.payment.GeneratePaymentQrCodeUseCase; +import br.com.fiap.grupo30.fastfood.infrastructure.auth.AdminRequired; import br.com.fiap.grupo30.fastfood.presentation.presenters.dto.CollectPaymentViaCashRequest; import br.com.fiap.grupo30.fastfood.presentation.presenters.dto.OrderDTO; import br.com.fiap.grupo30.fastfood.presentation.presenters.dto.PaymentQrCodeDTO; @@ -39,6 +40,7 @@ public ResponseEntity generateQrCodeForPaymentCollection( return ResponseEntity.ok().body(qrCode); } + @AdminRequired() @PostMapping(value = "/{orderId}/collect") @Operation(summary = "Collect payment by cash") public ResponseEntity collectPaymentByBash( diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/presentation/controllers/ProductController.java b/src/main/java/br/com/fiap/grupo30/fastfood/presentation/controllers/ProductController.java index afb7784..8845b17 100644 --- a/src/main/java/br/com/fiap/grupo30/fastfood/presentation/controllers/ProductController.java +++ b/src/main/java/br/com/fiap/grupo30/fastfood/presentation/controllers/ProductController.java @@ -1,6 +1,7 @@ package br.com.fiap.grupo30.fastfood.presentation.controllers; import br.com.fiap.grupo30.fastfood.domain.usecases.product.*; +import br.com.fiap.grupo30.fastfood.infrastructure.auth.AdminRequired; import br.com.fiap.grupo30.fastfood.presentation.presenters.dto.ProductDTO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -61,6 +62,7 @@ public ResponseEntity findProductById(@PathVariable Long id) { return ResponseEntity.ok().body(dto); } + @AdminRequired @PostMapping @Operation( summary = "Create a new product", @@ -81,6 +83,7 @@ public ResponseEntity createProduct(@RequestBody @Valid ProductDTO d return ResponseEntity.created(uri).body(dtoCreated); } + @AdminRequired() @PutMapping(value = PATH_VARIABLE_ID) @Operation( summary = "Update a product", @@ -98,6 +101,7 @@ public ResponseEntity updateProduct( return ResponseEntity.ok().body(dtoUpdated); } + @AdminRequired @DeleteMapping(value = PATH_VARIABLE_ID) @Operation( summary = "Delete a product",