-
Notifications
You must be signed in to change notification settings - Fork 0
Notification: fix build path, root-route through gateway, add JWT auth #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| using Microsoft.AspNetCore.Authorization; | ||
| using Microsoft.AspNetCore.Mvc; | ||
| using Notification.API.Services; | ||
| using Notification.Domain.Interfaces; | ||
|
|
@@ -6,7 +7,6 @@ | |
| namespace Notification.API.Controllers; | ||
|
|
||
| [ApiController] | ||
| [Route("api/[controller]")] | ||
| public class NotificationController : ControllerBase | ||
| { | ||
| private readonly INotificationRepository _repository; | ||
|
|
@@ -23,7 +23,8 @@ public NotificationController( | |
| _logger = logger; | ||
| } | ||
|
|
||
| [HttpGet] | ||
| [Authorize] | ||
| [HttpGet("/")] | ||
| public async Task<IActionResult> GetAll([FromQuery] int page = 1, [FromQuery] int pageSize = 20) | ||
| { | ||
| var notifications = await _repository.GetAllAsync(page, pageSize); | ||
|
|
@@ -41,7 +42,8 @@ public async Task<IActionResult> GetAll([FromQuery] int page = 1, [FromQuery] in | |
| })); | ||
| } | ||
|
|
||
| [HttpGet("{id:guid}")] | ||
| [Authorize] | ||
| [HttpGet("/{id:guid}")] | ||
| public async Task<IActionResult> GetById(Guid id) | ||
| { | ||
| var notification = await _repository.GetByIdAsync(id); | ||
|
|
@@ -68,7 +70,8 @@ public async Task<IActionResult> GetById(Guid id) | |
| /// Returns the rendered HTML email preview for a notification. | ||
| /// Use this endpoint to visually inspect notification output. | ||
| /// </summary> | ||
| [HttpGet("{id:guid}/preview")] | ||
| [Authorize] | ||
| [HttpGet("/{id:guid}/preview")] | ||
| [Produces("text/html")] | ||
| public async Task<IActionResult> GetPreview(Guid id) | ||
| { | ||
|
|
@@ -87,7 +90,7 @@ public async Task<IActionResult> GetPreview(Guid id) | |
| /// In production this would be a RabbitMQ consumer; this endpoint | ||
| /// enables local testing without a message broker. | ||
| /// </summary> | ||
| [HttpPost("events/order-placed")] | ||
| [HttpPost("/events/order-placed")] | ||
| public async Task<IActionResult> ReceiveOrderPlacedEvent([FromBody] OrderPlacedEventDto dto) | ||
| { | ||
| var orderEvent = new OrderPlacedEvent( | ||
|
|
@@ -101,7 +104,7 @@ public async Task<IActionResult> ReceiveOrderPlacedEvent([FromBody] OrderPlacedE | |
| return CreatedAtAction( | ||
| nameof(GetPreview), | ||
| new { id = notification.Id }, | ||
| new { notification.Id, PreviewUrl = $"/api/notification/{notification.Id}/preview" }); | ||
| new { notification.Id, PreviewUrl = $"/{notification.Id}/preview" }); | ||
| } | ||
|
Comment on lines
104
to
108
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚩 CreatedAtAction Location header will not be routable through the gateway The Was this helpful? React with 👍 or 👎 to provide feedback.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Acknowledged — this is a pre-existing gateway-prefix/forwarded-headers concern, not a regression from this PR. The old value was |
||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| #!/usr/bin/env bash | ||
| # | ||
| # E2E smoke test for the Notification service through the API gateway. | ||
| # | ||
| # Verifies (through http://localhost:5000, the gateway, which strips the | ||
| # /api/notifications prefix and forwards to notification-service:5005): | ||
| # 1. GET /api/notifications/healthz -> 200 (service reachable & healthy) | ||
| # 2. GET /api/notifications (no JWT) -> 401 (read endpoints are protected) | ||
| # | ||
| # Exits non-zero on any failure. | ||
| # | ||
| # Usage: | ||
| # src/Services/Notification/e2e-smoke.sh # uses default GATEWAY_URL | ||
| # GATEWAY_URL=http://localhost:5000 ./e2e-smoke.sh | ||
| # | ||
| set -euo pipefail | ||
|
|
||
| GATEWAY_URL="${GATEWAY_URL:-http://localhost:5000}" | ||
| fail=0 | ||
|
|
||
| check() { | ||
| local desc="$1" expected="$2" url="$3" | ||
| local actual | ||
| actual="$(curl -s -o /dev/null -w '%{http_code}' "$url" || echo "000")" | ||
| if [[ "$actual" == "$expected" ]]; then | ||
| echo "PASS: $desc ($url -> $actual)" | ||
| else | ||
| echo "FAIL: $desc ($url -> expected $expected, got $actual)" | ||
| fail=1 | ||
| fi | ||
| } | ||
|
|
||
| echo "== Notification E2E smoke (gateway: $GATEWAY_URL) ==" | ||
| check "health endpoint returns 200" 200 "$GATEWAY_URL/api/notifications/healthz" | ||
| check "list endpoint without token returns 401" 401 "$GATEWAY_URL/api/notifications" | ||
|
|
||
| if [[ "$fail" -ne 0 ]]; then | ||
| echo "SMOKE TEST FAILED" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "SMOKE TEST PASSED" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚩 Event ingestion endpoint is exposed through the gateway without authentication
The
ReceiveOrderPlacedEventPOST endpoint atNotificationController.cs:93does not have[Authorize], while all GET endpoints do. The comment explains this endpoint simulates a RabbitMQ consumer for local testing. However, the YARP gateway's catch-all route (/api/notifications/{**catch-all}atApiGateway/appsettings.json:33) will forwardPOST /api/notifications/events/order-placedto this endpoint, making it publicly reachable without any token. Anyone who can reach the gateway can create notifications with arbitrary data. If this is acceptable for the current development phase, consider adding a TODO or documenting the intent to protect or remove this endpoint before production.Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intentional for this phase. The
POST /events/order-placedendpoint simulates an internal message-broker (RabbitMQ) event — in production it'd be a broker consumer, not a gateway-exposed route. The task spec explicitly allows leaving intake unauthenticated; the security gate only requires the read/query endpoints (list/get/preview) to be[Authorize](401 without a token), which they are. Locking the intake down (or removing the gateway-exposed shim once a real broker is wired) is a follow-up, kept out of scope here to avoid regressing the local-testing flow.