Skip to content

Commit 8859924

Browse files
committed
Create the API's for handing phone API keys
1 parent ef77a32 commit 8859924

27 files changed

+648
-119
lines changed

api/pkg/di/container.go

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ func NewContainer(projectID string, version string) (container *Container) {
145145
container.RegisterDiscordRoutes()
146146
container.RegisterDiscordListeners()
147147

148+
container.RegisterPhoneAPIKeyRoutes()
149+
container.RegisterPhoneAPIKeyListeners()
150+
148151
container.RegisterMarketingListeners()
149152

150153
// this has to be last since it registers the /* route
@@ -191,18 +194,18 @@ func (container *Container) BearerAPIKeyMiddleware() fiber.Handler {
191194
return middlewares.BearerAPIKeyAuth(container.Logger(), container.Tracer(), container.UserRepository())
192195
}
193196

197+
// PhoneAPIKeyMiddleware creates a new instance of middlewares.BearerAPIKeyAuth
198+
func (container *Container) PhoneAPIKeyMiddleware() fiber.Handler {
199+
container.logger.Debug("creating middlewares.PhoneAPIKeyMiddleware")
200+
return middlewares.PhoneAPIKeyAuth(container.Logger(), container.Tracer(), container.PhoneAPIKeyRepository())
201+
}
202+
194203
// AuthenticatedMiddleware creates a new instance of middlewares.Authenticated
195204
func (container *Container) AuthenticatedMiddleware() fiber.Handler {
196205
container.logger.Debug("creating middlewares.Authenticated")
197206
return middlewares.Authenticated(container.Tracer())
198207
}
199208

200-
// AuthRouter creates router for authenticated requests
201-
func (container *Container) AuthRouter() fiber.Router {
202-
container.logger.Debug("creating authRouter")
203-
return container.App().Group("v1").Use(container.AuthenticatedMiddleware())
204-
}
205-
206209
// Logger creates a new instance of telemetry.Logger
207210
func (container *Container) Logger(skipFrameCount ...int) telemetry.Logger {
208211
container.logger.Debug("creating telemetry.Logger")
@@ -686,6 +689,17 @@ func (container *Container) MessageRepository() (repository repositories.Message
686689
)
687690
}
688691

692+
// PhoneAPIKeyRepository creates a new instance of repositories.PhoneAPIKeyRepository
693+
func (container *Container) PhoneAPIKeyRepository() (repository repositories.PhoneAPIKeyRepository) {
694+
container.logger.Debug("creating GORM repositories.PhoneAPIKeyRepository")
695+
return repositories.NewGormPhoneAPIKeyRepository(
696+
container.Logger(),
697+
container.Tracer(),
698+
container.DB(),
699+
container.UserRistrettoCache(),
700+
)
701+
}
702+
689703
// Integration3CXRepository creates a new instance of repositories.Integration3CxRepository
690704
func (container *Container) Integration3CXRepository() (repository repositories.Integration3CxRepository) {
691705
container.logger.Debug("creating GORM repositories.Integration3CxRepository")
@@ -1072,6 +1086,18 @@ func (container *Container) Integration3CXHandler() (handler *handlers.Integrati
10721086
)
10731087
}
10741088

1089+
// PhoneAPIKeyHandler creates a new instance of handlers.PhoneAPIKeyHandler
1090+
func (container *Container) PhoneAPIKeyHandler() (handler *handlers.PhoneAPIKeyHandler) {
1091+
container.logger.Debug(fmt.Sprintf("creating %T", handler))
1092+
1093+
return handlers.NewPhoneAPIKeyHandler(
1094+
container.Logger(),
1095+
container.Tracer(),
1096+
container.PhoneAPIKeyHandlerValidator(),
1097+
container.PhoneAPIKeyService(),
1098+
)
1099+
}
1100+
10751101
// DiscordHandler creates a new instance of handlers.DiscordHandler
10761102
func (container *Container) DiscordHandler() (handler *handlers.DiscordHandler) {
10771103
container.logger.Debug(fmt.Sprintf("creating %T", handler))
@@ -1097,6 +1123,15 @@ func (container *Container) LemonsqueezyHandlerValidator() (validator *validator
10971123
)
10981124
}
10991125

1126+
// PhoneAPIKeyHandlerValidator creates a new instance of validators.PhoneAPIKeyHandlerValidator
1127+
func (container *Container) PhoneAPIKeyHandlerValidator() (validator *validators.PhoneAPIKeyHandlerValidator) {
1128+
container.logger.Debug(fmt.Sprintf("creating %T", validator))
1129+
return validators.NewPhoneAPIKeyHandlerValidator(
1130+
container.Logger(),
1131+
container.Tracer(),
1132+
)
1133+
}
1134+
11001135
// LemonsqueezyClient creates a new instance of lemonsqueezy.Client
11011136
func (container *Container) LemonsqueezyClient() (client *lemonsqueezy.Client) {
11021137
container.logger.Debug(fmt.Sprintf("creating %T", client))
@@ -1129,6 +1164,12 @@ func (container *Container) RegisterIntegration3CXRoutes() {
11291164
container.Integration3CXHandler().RegisterRoutes(container.App(), container.BearerAPIKeyMiddleware(), container.AuthenticatedMiddleware())
11301165
}
11311166

1167+
// RegisterPhoneAPIKeyRoutes registers routes for the /phone-api-key prefix
1168+
func (container *Container) RegisterPhoneAPIKeyRoutes() {
1169+
container.logger.Debug(fmt.Sprintf("registering [%T] routes", &handlers.Integration3CXHandler{}))
1170+
container.PhoneAPIKeyHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware())
1171+
}
1172+
11321173
// RegisterDiscordRoutes registers routes for the /discord prefix
11331174
func (container *Container) RegisterDiscordRoutes() {
11341175
container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.DiscordHandler{}))
@@ -1261,6 +1302,20 @@ func (container *Container) RegisterIntegration3CXListeners() {
12611302
}
12621303
}
12631304

1305+
// RegisterPhoneAPIKeyListeners registers event listeners for listeners.PhoneAPIKeyListener
1306+
func (container *Container) RegisterPhoneAPIKeyListeners() {
1307+
container.logger.Debug(fmt.Sprintf("registering listeners for %T", listeners.PhoneAPIKeyListener{}))
1308+
_, routes := listeners.NewPhoneAPIKeyListener(
1309+
container.Logger(),
1310+
container.Tracer(),
1311+
container.PhoneAPIKeyService(),
1312+
)
1313+
1314+
for event, handler := range routes {
1315+
container.EventDispatcher().Subscribe(event, handler)
1316+
}
1317+
}
1318+
12641319
// RegisterWebhookListeners registers event listeners for listeners.WebhookListener
12651320
func (container *Container) RegisterWebhookListeners() {
12661321
container.logger.Debug(fmt.Sprintf("registering listeners for %T", listeners.WebhookListener{}))
@@ -1287,6 +1342,17 @@ func (container *Container) MessageService() (service *services.MessageService)
12871342
)
12881343
}
12891344

1345+
// PhoneAPIKeyService creates a new instance of services.PhoneAPIKeyService
1346+
func (container *Container) PhoneAPIKeyService() (service *services.PhoneAPIKeyService) {
1347+
container.logger.Debug(fmt.Sprintf("creating %T", service))
1348+
return services.NewPhoneAPIKeyService(
1349+
container.Logger(),
1350+
container.Tracer(),
1351+
container.PhoneRepository(),
1352+
container.PhoneAPIKeyRepository(),
1353+
)
1354+
}
1355+
12901356
// NotificationService creates a new instance of services.PhoneNotificationService
12911357
func (container *Container) NotificationService() (service *services.PhoneNotificationService) {
12921358
container.logger.Debug(fmt.Sprintf("creating %T", service))
@@ -1303,31 +1369,33 @@ func (container *Container) NotificationService() (service *services.PhoneNotifi
13031369
// RegisterMessageRoutes registers routes for the /messages prefix
13041370
func (container *Container) RegisterMessageRoutes() {
13051371
container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.MessageHandler{}))
1306-
container.MessageHandler().RegisterRoutes(container.AuthRouter())
1372+
container.MessageHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware())
1373+
container.MessageHandler().RegisterPhoneAPIKeyRoutes(container.App(), container.PhoneAPIKeyMiddleware(), container.AuthenticatedMiddleware())
13071374
}
13081375

13091376
// RegisterBulkMessageRoutes registers routes for the /bulk-messages prefix
13101377
func (container *Container) RegisterBulkMessageRoutes() {
13111378
container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.BulkMessageHandler{}))
1312-
container.BulkMessageHandler().RegisterRoutes(container.AuthRouter())
1379+
container.BulkMessageHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware())
13131380
}
13141381

13151382
// RegisterMessageThreadRoutes registers routes for the /message-threads prefix
13161383
func (container *Container) RegisterMessageThreadRoutes() {
13171384
container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.MessageThreadHandler{}))
1318-
container.MessageThreadHandler().RegisterRoutes(container.AuthRouter())
1385+
container.MessageThreadHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware())
13191386
}
13201387

13211388
// RegisterHeartbeatRoutes registers routes for the /heartbeats prefix
13221389
func (container *Container) RegisterHeartbeatRoutes() {
13231390
container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.HeartbeatHandler{}))
1324-
container.HeartbeatHandler().RegisterRoutes(container.AuthRouter())
1391+
container.HeartbeatHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware())
1392+
container.HeartbeatHandler().RegisterPhoneAPIKeyRoutes(container.App(), container.PhoneAPIKeyMiddleware(), container.AuthenticatedMiddleware())
13251393
}
13261394

13271395
// RegisterBillingRoutes registers routes for the /billing prefix
13281396
func (container *Container) RegisterBillingRoutes() {
13291397
container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.BillingHandler{}))
1330-
container.BillingHandler().RegisterRoutes(container.AuthRouter())
1398+
container.BillingHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware())
13311399
}
13321400

13331401
// RegisterWebhookRoutes registers routes for the /webhooks prefix
@@ -1339,19 +1407,20 @@ func (container *Container) RegisterWebhookRoutes() {
13391407
// RegisterPhoneRoutes registers routes for the /phone prefix
13401408
func (container *Container) RegisterPhoneRoutes() {
13411409
container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.PhoneHandler{}))
1342-
container.PhoneHandler().RegisterRoutes(container.AuthRouter())
1410+
container.PhoneHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware())
1411+
container.PhoneHandler().RegisterPhoneAPIKeyRoutes(container.App(), container.PhoneAPIKeyMiddleware(), container.AuthenticatedMiddleware())
13431412
}
13441413

13451414
// RegisterUserRoutes registers routes for the /users prefix
13461415
func (container *Container) RegisterUserRoutes() {
13471416
container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.UserHandler{}))
1348-
container.UserHandler().RegisterRoutes(container.AuthRouter())
1417+
container.UserHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware())
13491418
}
13501419

13511420
// RegisterEventRoutes registers routes for the /events prefix
13521421
func (container *Container) RegisterEventRoutes() {
13531422
container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.EventsHandler{}))
1354-
container.EventsHandler().RegisterRoutes(container.AuthRouter())
1423+
container.EventsHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware())
13551424
}
13561425

13571426
// RegisterSwaggerRoutes registers routes for swagger

api/pkg/events/phone_updated_event.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ const EventTypePhoneUpdated = "phone.updated"
1212

1313
// PhoneUpdatedPayload is the payload of the EventTypePhoneUpdated event
1414
type PhoneUpdatedPayload struct {
15-
PhoneID uuid.UUID `json:"phone_id"`
16-
UserID entities.UserID `json:"user_id"`
17-
Timestamp time.Time `json:"timestamp"`
18-
Owner string `json:"owner"`
19-
SIM entities.SIM `json:"sim"`
15+
PhoneID uuid.UUID `json:"phone_id"`
16+
UserID entities.UserID `json:"user_id"`
17+
PhoneAPIKeyID *uuid.UUID `json:"phone_api_key_id"`
18+
Timestamp time.Time `json:"timestamp"`
19+
Owner string `json:"owner"`
20+
SIM entities.SIM `json:"sim"`
2021
}

api/pkg/handlers/billing_handler.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ func NewBillingHandler(
3737
}
3838

3939
// RegisterRoutes registers the routes for the MessageHandler
40-
func (h *BillingHandler) RegisterRoutes(router fiber.Router) {
41-
router.Get("/billing/usage-history", h.UsageHistory)
42-
router.Get("/billing/usage", h.Usage)
40+
func (h *BillingHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.Handler) {
41+
router.Get("/billing/usage-history", h.computeRoute(middlewares, h.UsageHistory)...)
42+
router.Get("/billing/usage", h.computeRoute(middlewares, h.Usage)...)
4343
}
4444

4545
// UsageHistory returns the usage history of a user

api/pkg/handlers/bulk_message_handler.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ func NewBulkMessageHandler(
4343
}
4444

4545
// RegisterRoutes registers the routes for the MessageHandler
46-
func (h *BulkMessageHandler) RegisterRoutes(router fiber.Router) {
47-
router.Post("/bulk-messages", h.Store)
46+
func (h *BulkMessageHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.Handler) {
47+
router.Post("/v1/bulk-messages", h.computeRoute(middlewares, h.Store)...)
4848
}
4949

5050
// Store sends bulk SMS messages from a CSV file.

api/pkg/handlers/events_handler.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ func NewEventsHandler(
3737
}
3838

3939
// RegisterRoutes registers the routes for the MessageHandler
40-
func (h *EventsHandler) RegisterRoutes(router fiber.Router) {
41-
router.Post("/events", h.Dispatch)
40+
func (h *EventsHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.Handler) {
41+
router.Post("/v1/events", h.computeRoute(middlewares, h.Dispatch)...)
4242
}
4343

4444
// Dispatch a cloud event

api/pkg/handlers/handler.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package handlers
22

33
import (
4+
"fmt"
45
"net/url"
6+
"slices"
7+
"strings"
58

69
"github.com/NdoleStudio/httpsms/pkg/entities"
710
"github.com/NdoleStudio/httpsms/pkg/middlewares"
@@ -35,6 +38,14 @@ func (h *handler) responseUnauthorized(c *fiber.Ctx) error {
3538
})
3639
}
3740

41+
func (h *handler) responsePhoneAPIKeyUnauthorized(c *fiber.Ctx, owner string, authCtx entities.AuthContext) error {
42+
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
43+
"status": "error",
44+
"message": "You are not authorized to carry out the request for this phone number",
45+
"data": fmt.Sprintf("The phone API key is does not have permission to carry out actions on the phone number [%s]. The API key is only configured for these phone numbers [%s]", owner, strings.Join(authCtx.PhoneNumbers, ",")),
46+
})
47+
}
48+
3849
func (h *handler) responseForbidden(c *fiber.Ctx) error {
3950
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
4051
"status": "error",
@@ -127,3 +138,11 @@ func (h *handler) mergeErrors(errors ...url.Values) url.Values {
127138
}
128139
return result
129140
}
141+
142+
func (h *handler) authorizePhoneAPIKey(c *fiber.Ctx, phoneNumber string) bool {
143+
user := h.userFromContext(c)
144+
if user.PhoneAPIKeyID == nil {
145+
return true
146+
}
147+
return slices.Contains(user.PhoneNumbers, phoneNumber)
148+
}

api/pkg/handlers/heartbeat_handler.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,14 @@ func NewHeartbeatHandler(
3939
}
4040
}
4141

42-
// RegisterRoutes registers the routes for the MessageHandler
43-
func (h *HeartbeatHandler) RegisterRoutes(router fiber.Router) {
44-
router.Get("/heartbeats", h.Index)
45-
router.Post("/heartbeats", h.Store)
42+
// RegisterRoutes registers the routes for the HeartbeatHandler
43+
func (h *HeartbeatHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.Handler) {
44+
router.Get("/heartbeats", h.computeRoute(middlewares, h.Index)...)
45+
}
46+
47+
// RegisterPhoneAPIKeyRoutes registers the routes for the HeartbeatHandler
48+
func (h *HeartbeatHandler) RegisterPhoneAPIKeyRoutes(router fiber.Router, middlewares ...fiber.Handler) {
49+
router.Post("/heartbeats", h.computeRoute(middlewares, h.Store)...)
4650
}
4751

4852
// Index returns the heartbeats of a phone number
@@ -124,6 +128,13 @@ func (h *HeartbeatHandler) Store(c *fiber.Ctx) error {
124128
return h.responseUnprocessableEntity(c, errors, "validation errors while storing heartbeat")
125129
}
126130

131+
for _, phoneNumber := range request.PhoneNumbers {
132+
if !h.authorizePhoneAPIKey(c, phoneNumber) {
133+
ctxLogger.Warn(stacktrace.NewError(fmt.Sprintf("phone API Key ID [%s] is not authorized to store heartbeat for phone number [%s]", h.userFromContext(c).PhoneAPIKeyID, phoneNumber)))
134+
return h.responsePhoneAPIKeyUnauthorized(c, phoneNumber, h.userFromContext(c))
135+
}
136+
}
137+
127138
params := request.ToStoreParams(h.userFromContext(c), c.OriginalURL(), c.Get("X-Client-Version"))
128139

129140
wg := sync.WaitGroup{}

0 commit comments

Comments
 (0)