Api key configuracion and quotes endpooint
This commit is contained in:
@ -36,6 +36,10 @@ type Service struct {
|
|||||||
AuthorizedServices map[string]AuthorizedService `toml:"AuthorizedServices"`
|
AuthorizedServices map[string]AuthorizedService `toml:"AuthorizedServices"`
|
||||||
APIBasePort string
|
APIBasePort string
|
||||||
EnableJWTAuth bool // Enable JWT authentication for service-to-service communication
|
EnableJWTAuth bool // Enable JWT authentication for service-to-service communication
|
||||||
|
// ServiceAPIKey is the shared secret that authenticates qbymarouter (and any
|
||||||
|
// other internal service) when posting to /qfixdpl/v1/quotes and
|
||||||
|
// /qfixdpl/v1/messages. Must match the DPLAPIKey configured on the caller.
|
||||||
|
ServiceAPIKey string
|
||||||
FIX FIXConfig
|
FIX FIXConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -348,7 +348,7 @@ func (cont *Controller) AllMessages(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.APIKey != "1234" {
|
if !cont.checkServiceAPIKey(req.APIKey) {
|
||||||
ctx.JSON(http.StatusUnauthorized, HTTPError{Error: "Not allowed to perform this request"})
|
ctx.JSON(http.StatusUnauthorized, HTTPError{Error: "Not allowed to perform this request"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -356,6 +356,13 @@ func (cont *Controller) AllMessages(ctx *gin.Context) {
|
|||||||
ctx.JSON(http.StatusOK, cont.tradeProvider.GetAllMessages(req.In, req.Out))
|
ctx.JSON(http.StatusOK, cont.tradeProvider.GetAllMessages(req.In, req.Out))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkServiceAPIKey returns true when the provided key matches the configured
|
||||||
|
// service-to-service shared secret. Empty configured key is always rejected
|
||||||
|
// to avoid open authentication when misconfigured.
|
||||||
|
func (cont *Controller) checkServiceAPIKey(key string) bool {
|
||||||
|
return cont.config.ServiceAPIKey != "" && key == cont.config.ServiceAPIKey
|
||||||
|
}
|
||||||
|
|
||||||
// GetPendingQuoteRequests godoc
|
// GetPendingQuoteRequests godoc
|
||||||
// @Summary List pending QuoteRequests
|
// @Summary List pending QuoteRequests
|
||||||
// @Description Returns all QuoteRequests received from TW that have not been quoted yet by the dealer
|
// @Description Returns all QuoteRequests received from TW that have not been quoted yet by the dealer
|
||||||
@ -382,12 +389,19 @@ func (cont *Controller) GetPendingQuoteRequests(ctx *gin.Context) {
|
|||||||
// @Failure 500 {object} HTTPError
|
// @Failure 500 {object} HTTPError
|
||||||
// @Router /qfixdpl/v1/quotes [post]
|
// @Router /qfixdpl/v1/quotes [post]
|
||||||
func (cont *Controller) SendQuote(ctx *gin.Context) {
|
func (cont *Controller) SendQuote(ctx *gin.Context) {
|
||||||
|
setHeaders(ctx, cont.config)
|
||||||
|
|
||||||
var req SendQuoteRequest
|
var req SendQuoteRequest
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
ctx.JSON(http.StatusBadRequest, HTTPError{Error: err.Error()})
|
ctx.JSON(http.StatusBadRequest, HTTPError{Error: err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !cont.checkServiceAPIKey(req.APIKey) {
|
||||||
|
ctx.JSON(http.StatusUnauthorized, HTTPError{Error: "Not allowed to perform this request"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
price, err := decimal.NewFromString(req.Price)
|
price, err := decimal.NewFromString(req.Price)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(http.StatusBadRequest, HTTPError{Error: "invalid price: " + err.Error()})
|
ctx.JSON(http.StatusBadRequest, HTTPError{Error: "invalid price: " + err.Error()})
|
||||||
|
|||||||
@ -18,6 +18,7 @@ type Session struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SendQuoteRequest struct {
|
type SendQuoteRequest struct {
|
||||||
|
APIKey string `json:"APIKey" binding:"required"`
|
||||||
QuoteReqID string `json:"QuoteReqID" binding:"required"`
|
QuoteReqID string `json:"QuoteReqID" binding:"required"`
|
||||||
Price string `json:"Price" binding:"required" example:"99.6"`
|
Price string `json:"Price" binding:"required" example:"99.6"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,10 +24,11 @@ func SetRoutes(api *API) {
|
|||||||
qfixdpl.GET("/trades", cont.GetTrades)
|
qfixdpl.GET("/trades", cont.GetTrades)
|
||||||
qfixdpl.GET("/trades/:quoteReqID/logs", cont.GetLogs)
|
qfixdpl.GET("/trades/:quoteReqID/logs", cont.GetLogs)
|
||||||
qfixdpl.GET("/quote-requests", cont.GetPendingQuoteRequests)
|
qfixdpl.GET("/quote-requests", cont.GetPendingQuoteRequests)
|
||||||
qfixdpl.POST("/quotes", cont.SendQuote)
|
|
||||||
|
|
||||||
msgs := v1.Group("/")
|
// services group: API-key auth via body, no session cookie required.
|
||||||
msgs.POST("/messages", cont.AllMessages)
|
services := v1.Group("/")
|
||||||
|
services.POST("/messages", cont.AllMessages)
|
||||||
|
services.POST("/quotes", cont.SendQuote)
|
||||||
|
|
||||||
backoffice := qfixdpl.Group("/backoffice")
|
backoffice := qfixdpl.Group("/backoffice")
|
||||||
backoffice.Use(cont.BackOfficeUser)
|
backoffice.Use(cont.BackOfficeUser)
|
||||||
|
|||||||
@ -39,6 +39,10 @@ type Config struct {
|
|||||||
AuthorizedServices map[string]app.AuthorizedService `toml:"AuthorizedServices"`
|
AuthorizedServices map[string]app.AuthorizedService `toml:"AuthorizedServices"`
|
||||||
Port string
|
Port string
|
||||||
EnableJWTAuth bool
|
EnableJWTAuth bool
|
||||||
|
// ServiceAPIKey authenticates internal services (qbymarouter, etc.) calling
|
||||||
|
// /qfixdpl/v1/quotes and /qfixdpl/v1/messages. Compared against the APIKey
|
||||||
|
// field in the request body.
|
||||||
|
ServiceAPIKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(userData app.UserDataProvider, storeInstance *store.Store, tradeProvider TradeProvider, config Config, notify domain.Notifier) *API {
|
func New(userData app.UserDataProvider, storeInstance *store.Store, tradeProvider TradeProvider, config Config, notify domain.Notifier) *API {
|
||||||
|
|||||||
@ -51,6 +51,7 @@ func Runner(cfg app.Config) error {
|
|||||||
External: cfg.External,
|
External: cfg.External,
|
||||||
AuthorizedServices: cfg.AuthorizedServices,
|
AuthorizedServices: cfg.AuthorizedServices,
|
||||||
EnableJWTAuth: cfg.EnableJWTAuth,
|
EnableJWTAuth: cfg.EnableJWTAuth,
|
||||||
|
ServiceAPIKey: cfg.ServiceAPIKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
api := rest.New(userData, appStore, fixManager, apiConfig, notify)
|
api := rest.New(userData, appStore, fixManager, apiConfig, notify)
|
||||||
|
|||||||
Reference in New Issue
Block a user