@@ -9,22 +9,29 @@ import (
99 "strings"
1010 "sync"
1111 "testing"
12+ "time"
13+
14+ "github.com/golang-jwt/jwt/v5"
1215)
1316
17+ const testGatewayPhoneID = "11111111-1111-1111-1111-111111111111"
18+
1419func TestRecordNotificationCopiesRecords (t * testing.T ) {
1520 t .Parallel ()
1621
1722 instance := newEmulator ("http://api.example" , http .DefaultClient )
1823 instance .registerGateway ("gateway-1" , gatewayRegistration {
1924 PhoneNumber : "+18005550199" ,
2025 PhoneAPIKey : "phone-key" ,
26+ PhoneID : testGatewayPhoneID ,
2127 })
2228
2329 record := instance .recordNotification (
2430 "gateway-1" ,
2531 map [string ]string {"KEY_MESSAGE_ID" : "message-1" },
2632 "message" ,
2733 "message-1" ,
34+ "Bearer test-token" ,
2835 )
2936 instance .markNotificationProcessed (record )
3037
@@ -90,6 +97,7 @@ func TestNotificationHandlerProcessesMessage(t *testing.T) {
9097 instance .registerGateway ("gateway-1" , gatewayRegistration {
9198 PhoneNumber : "+18005550199" ,
9299 PhoneAPIKey : "phone-key" ,
100+ PhoneID : testGatewayPhoneID ,
93101 })
94102
95103 body := callbackBody (t , map [string ]string {"KEY_MESSAGE_ID" : "message-1" })
@@ -98,6 +106,7 @@ func TestNotificationHandlerProcessesMessage(t *testing.T) {
98106 "/notifications/gateway-1" ,
99107 bytes .NewReader (body ),
100108 )
109+ request .Header .Set ("Authorization" , validNotificationToken (t , testGatewayPhoneID ))
101110 response := httptest .NewRecorder ()
102111 instance .notificationHandler ().ServeHTTP (response , request )
103112 if response .Code != http .StatusNoContent {
@@ -146,13 +155,15 @@ func TestNotificationHandlerStoresHeartbeat(t *testing.T) {
146155 instance .registerGateway ("gateway-1" , gatewayRegistration {
147156 PhoneNumber : "+18005550199" ,
148157 PhoneAPIKey : "phone-key" ,
158+ PhoneID : testGatewayPhoneID ,
149159 })
150160
151161 request := httptest .NewRequest (
152162 http .MethodPost ,
153163 "/notifications/gateway-1" ,
154164 bytes .NewReader (callbackBody (t , map [string ]string {"KEY_HEARTBEAT_ID" : "heartbeat-1" })),
155165 )
166+ request .Header .Set ("Authorization" , validNotificationToken (t , testGatewayPhoneID ))
156167 response := httptest .NewRecorder ()
157168 instance .notificationHandler ().ServeHTTP (response , request )
158169 if response .Code != http .StatusNoContent {
@@ -184,13 +195,15 @@ func TestNotificationHandlerRetainsProcessingFailure(t *testing.T) {
184195 instance .registerGateway ("gateway-1" , gatewayRegistration {
185196 PhoneNumber : "+18005550199" ,
186197 PhoneAPIKey : "phone-key" ,
198+ PhoneID : testGatewayPhoneID ,
187199 })
188200
189201 request := httptest .NewRequest (
190202 http .MethodPost ,
191203 "/notifications/gateway-1" ,
192204 bytes .NewReader (callbackBody (t , map [string ]string {"KEY_MESSAGE_ID" : "message-1" })),
193205 )
206+ request .Header .Set ("Authorization" , validNotificationToken (t , testGatewayPhoneID ))
194207 response := httptest .NewRecorder ()
195208 instance .notificationHandler ().ServeHTTP (response , request )
196209 if response .Code != http .StatusInternalServerError {
@@ -250,17 +263,20 @@ func TestNotificationHandlerProcessesRetryAfterFailure(t *testing.T) {
250263 instance .registerGateway ("gateway-1" , gatewayRegistration {
251264 PhoneNumber : "+18005550199" ,
252265 PhoneAPIKey : "phone-key" ,
266+ PhoneID : testGatewayPhoneID ,
253267 })
254268 handler := instance .notificationHandler ()
255269 body := callbackBody (t , map [string ]string {"KEY_MESSAGE_ID" : "message-1" })
256270
257271 firstRequest := httptest .NewRequest (http .MethodPost , "/notifications/gateway-1" , bytes .NewReader (body ))
272+ firstRequest .Header .Set ("Authorization" , validNotificationToken (t , testGatewayPhoneID ))
258273 firstResponse := httptest .NewRecorder ()
259274 handler .ServeHTTP (firstResponse , firstRequest )
260275 if firstResponse .Code != http .StatusInternalServerError {
261276 t .Fatalf ("first callback status = %d, want 500: %s" , firstResponse .Code , firstResponse .Body .String ())
262277 }
263278 secondRequest := httptest .NewRequest (http .MethodPost , "/notifications/gateway-1" , bytes .NewReader (body ))
279+ secondRequest .Header .Set ("Authorization" , validNotificationToken (t , testGatewayPhoneID ))
264280 secondResponse := httptest .NewRecorder ()
265281 handler .ServeHTTP (secondResponse , secondRequest )
266282 if secondResponse .Code != http .StatusNoContent {
@@ -315,6 +331,7 @@ func TestControlHandlerRegistersGatewayAndReceivesIncomingMessage(t *testing.T)
315331 registration := performJSONRequest (t , handler , http .MethodPut , "/test/gateways/gateway-1" , map [string ]any {
316332 "phone_number" : "+18005550199" ,
317333 "phone_api_key" : "phone-key" ,
334+ "phone_id" : testGatewayPhoneID ,
318335 })
319336 if registration .Code != http .StatusNoContent {
320337 t .Fatalf ("registration status = %d, want 204: %s" , registration .Code , registration .Body .String ())
@@ -368,18 +385,21 @@ func TestControlHandlerFiltersNotificationRecordsByMessageID(t *testing.T) {
368385 instance .registerGateway ("gateway-1" , gatewayRegistration {
369386 PhoneNumber : "+18005550199" ,
370387 PhoneAPIKey : "phone-key" ,
388+ PhoneID : testGatewayPhoneID ,
371389 })
372390 instance .recordNotification (
373391 "gateway-1" ,
374392 map [string ]string {"KEY_MESSAGE_ID" : "message-1" },
375393 "message" ,
376394 "message-1" ,
395+ "Bearer test-token" ,
377396 )
378397 instance .recordNotification (
379398 "gateway-1" ,
380399 map [string ]string {"KEY_MESSAGE_ID" : "message-2" },
381400 "message" ,
382401 "message-2" ,
402+ "Bearer test-token" ,
383403 )
384404
385405 response := httptest .NewRecorder ()
@@ -406,6 +426,76 @@ func TestControlHandlerFiltersNotificationRecordsByMessageID(t *testing.T) {
406426 }
407427}
408428
429+ func TestNotificationHandlerRejectsMissingAuthorization (t * testing.T ) {
430+ t .Parallel ()
431+
432+ instance := newEmulator ("http://api.example" , http .DefaultClient )
433+ instance .registerGateway ("gateway-1" , gatewayRegistration {
434+ PhoneNumber : "+18005550199" ,
435+ PhoneAPIKey : "phone-key" ,
436+ PhoneID : testGatewayPhoneID ,
437+ })
438+
439+ request := httptest .NewRequest (
440+ http .MethodPost ,
441+ "/notifications/gateway-1" ,
442+ bytes .NewReader (callbackBody (t , map [string ]string {"KEY_MESSAGE_ID" : "message-1" })),
443+ )
444+ response := httptest .NewRecorder ()
445+ instance .notificationHandler ().ServeHTTP (response , request )
446+ if response .Code != http .StatusUnauthorized {
447+ t .Fatalf ("callback status = %d, want 401: %s" , response .Code , response .Body .String ())
448+ }
449+ if records := instance .listGatewayRecords ("gateway-1" ); len (records ) != 0 {
450+ t .Fatalf ("record count = %d, want 0 for an unauthenticated request" , len (records ))
451+ }
452+ }
453+
454+ func TestNotificationHandlerRejectsTokenSignedWithWrongSecret (t * testing.T ) {
455+ t .Parallel ()
456+
457+ instance := newEmulator ("http://api.example" , http .DefaultClient )
458+ instance .registerGateway ("gateway-1" , gatewayRegistration {
459+ PhoneNumber : "+18005550199" ,
460+ PhoneAPIKey : "phone-key" ,
461+ PhoneID : testGatewayPhoneID ,
462+ })
463+
464+ request := httptest .NewRequest (
465+ http .MethodPost ,
466+ "/notifications/gateway-1" ,
467+ bytes .NewReader (callbackBody (t , map [string ]string {"KEY_MESSAGE_ID" : "message-1" })),
468+ )
469+ request .Header .Set ("Authorization" , validNotificationToken (t , "some-other-phone-id" ))
470+ response := httptest .NewRecorder ()
471+ instance .notificationHandler ().ServeHTTP (response , request )
472+ if response .Code != http .StatusUnauthorized {
473+ t .Fatalf ("callback status = %d, want 401: %s" , response .Code , response .Body .String ())
474+ }
475+ if records := instance .listGatewayRecords ("gateway-1" ); len (records ) != 0 {
476+ t .Fatalf ("record count = %d, want 0 for a request signed with the wrong secret" , len (records ))
477+ }
478+ }
479+
480+ func validNotificationToken (t * testing.T , phoneID string ) string {
481+ t .Helper ()
482+
483+ now := time .Now ().UTC ()
484+ token := jwt .NewWithClaims (jwt .SigningMethodHS256 , jwt.RegisteredClaims {
485+ Audience : []string {"https://adapter-emulator:9091/notifications/gateway-1" },
486+ ExpiresAt : jwt .NewNumericDate (now .Add (10 * time .Minute )),
487+ IssuedAt : jwt .NewNumericDate (now ),
488+ Issuer : notificationJWTIssuer ,
489+ NotBefore : jwt .NewNumericDate (now .Add (- 10 * time .Minute )),
490+ Subject : phoneID ,
491+ })
492+ signed , err := token .SignedString ([]byte (phoneID ))
493+ if err != nil {
494+ t .Fatalf ("sign notification token: %v" , err )
495+ }
496+ return "Bearer " + signed
497+ }
498+
409499func callbackBody (t * testing.T , data map [string ]string ) []byte {
410500 t .Helper ()
411501
0 commit comments