Skip to content

Commit

Permalink
Merge pull request #443 from BuxOrg/rc-v1.1
Browse files Browse the repository at this point in the history
Release candidate v1.1.0
  • Loading branch information
jakubmkowalski authored Feb 13, 2024
2 parents 1ece69d + a3b02f3 commit dd7aec2
Show file tree
Hide file tree
Showing 28 changed files with 672 additions and 195 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,9 @@ data/

# Configuration files
.env.config

# VSCode
.vscode/

# Ignore config file
config.yaml
5 changes: 0 additions & 5 deletions .vscode/extensions.json

This file was deleted.

14 changes: 0 additions & 14 deletions .vscode/launch.json

This file was deleted.

23 changes: 0 additions & 23 deletions .vscode/settings.json

This file was deleted.

17 changes: 0 additions & 17 deletions .vscode/tasks.json

This file was deleted.

86 changes: 0 additions & 86 deletions actions/admin/block_headers.go

This file was deleted.

2 changes: 0 additions & 2 deletions actions/admin/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ func RegisterRoutes(router *apirouter.Router, appConfig *config.AppConfig, servi
router.HTTPRouter.GET("/"+config.APIVersion+"/admin/status", action.Request(router, require.Wrap(action.status)))
router.HTTPRouter.POST("/"+config.APIVersion+"/admin/access-keys/search", action.Request(router, require.Wrap(action.accessKeysSearch)))
router.HTTPRouter.POST("/"+config.APIVersion+"/admin/access-keys/count", action.Request(router, require.Wrap(action.accessKeysCount)))
router.HTTPRouter.POST("/"+config.APIVersion+"/admin/block-headers/search", action.Request(router, require.Wrap(action.blockHeadersSearch)))
router.HTTPRouter.POST("/"+config.APIVersion+"/admin/block-headers/count", action.Request(router, require.Wrap(action.blockHeadersCount)))
router.HTTPRouter.POST("/"+config.APIVersion+"/admin/destinations/search", action.Request(router, require.Wrap(action.destinationsSearch)))
router.HTTPRouter.POST("/"+config.APIVersion+"/admin/destinations/count", action.Request(router, require.Wrap(action.destinationsCount)))
router.HTTPRouter.POST("/"+config.APIVersion+"/admin/paymail/get", action.Request(router, require.Wrap(action.paymailGetAddress)))
Expand Down
1 change: 0 additions & 1 deletion actions/destinations/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ func (a *Action) create(w http.ResponseWriter, req *http.Request, _ httprouter.P
xPub.RawXpub(),
uint32(0), // todo: use a constant? protect this?
scriptType,
true, // monitor this address as it was created by request of a user to share
opts...,
); err != nil {
apirouter.ReturnResponse(w, req, http.StatusUnprocessableEntity, err.Error())
Expand Down
45 changes: 39 additions & 6 deletions actions/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package actions

import (
"net/http"
"strings"
"time"

"github.com/BuxOrg/bux"
Expand All @@ -21,14 +22,14 @@ type Action struct {

// NewStack is used for registering routes
func NewStack(appConfig *config.AppConfig,
services *config.AppServices) (Action, *apirouter.InternalStack) {
services *config.AppServices,
) (Action, *apirouter.InternalStack) {
return Action{AppConfig: appConfig, Services: services}, apirouter.NewStack()
}

// RequireAuthentication checks and requires authentication for the related method
func (a *Action) RequireAuthentication(fn httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {

// Check the authentication
var knownErr dictionary.ErrorMessage
if req, knownErr = CheckAuthentication(a.AppConfig, a.Services.Bux, req, false, true); knownErr.Code > 0 {
Expand All @@ -44,7 +45,6 @@ func (a *Action) RequireAuthentication(fn httprouter.Handle) httprouter.Handle {
// RequireBasicAuthentication checks and requires authentication for the related method, but does not require signing
func (a *Action) RequireBasicAuthentication(fn httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {

// Check the authentication
var knownErr dictionary.ErrorMessage
if req, knownErr = CheckAuthentication(a.AppConfig, a.Services.Bux, req, false, false); knownErr.Code > 0 {
Expand All @@ -60,7 +60,6 @@ func (a *Action) RequireBasicAuthentication(fn httprouter.Handle) httprouter.Han
// RequireAdminAuthentication checks and requires ADMIN authentication for the related method
func (a *Action) RequireAdminAuthentication(fn httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {

// Check the authentication
var knownErr dictionary.ErrorMessage
if req, knownErr = CheckAuthentication(a.AppConfig, a.Services.Bux, req, true, true); knownErr.Code > 0 {
Expand All @@ -73,15 +72,29 @@ func (a *Action) RequireAdminAuthentication(fn httprouter.Handle) httprouter.Han
}
}

// RequireCallbackAuthentication checks and requires callback Authorize Bearer token for the related method
func (a *Action) RequireCallbackAuthentication(fn httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
var knownErr dictionary.ErrorMessage
if req, knownErr = VerifyCallbackToken(a.AppConfig, req); knownErr.Code > 0 {
ReturnErrorResponse(w, req, knownErr, "")
return
}

// Continue to next method
fn(w, req, p)
}
}

// Request will process the request in the router
func (a *Action) Request(_ *apirouter.Router, h httprouter.Handle) httprouter.Handle {
return Request(h, a)
}

// CheckAuthentication will check the authentication
func CheckAuthentication(appConfig *config.AppConfig, bux bux.ClientInterface, req *http.Request,
adminRequired bool, requireSigning bool) (*http.Request, dictionary.ErrorMessage) {

adminRequired bool, requireSigning bool,
) (*http.Request, dictionary.ErrorMessage) {
// Bad/Unknown scheme
if appConfig.Authentication.Scheme != config.AuthenticationSchemeXpub {
return req, dictionary.GetError(dictionary.ErrorAuthenticationScheme, appConfig.Authentication.Scheme)
Expand All @@ -102,6 +115,26 @@ func CheckAuthentication(appConfig *config.AppConfig, bux bux.ClientInterface, r
return req, dictionary.ErrorMessage{}
}

// VerifyCallbackToken verifies the callback token - if it's valid and matches the Bearer scheme.
func VerifyCallbackToken(appConfig *config.AppConfig, req *http.Request) (*http.Request, dictionary.ErrorMessage) {
const BearerSchema = "Bearer "
authHeader := req.Header.Get("Authorization")
if authHeader == "" {
return req, dictionary.GetError(dictionary.ErrorAuthenticationCallback, "missing auth header")
}

if !strings.HasPrefix(authHeader, BearerSchema) || len(authHeader) <= len(BearerSchema) {
return req, dictionary.GetError(dictionary.ErrorAuthenticationCallback, "invalid or missing bearer token")
}

providedToken := authHeader[len(BearerSchema):]
if providedToken != appConfig.Nodes.Callback.CallbackToken {
return req, dictionary.GetError(dictionary.ErrorAuthenticationCallback, "invalid authorization token")
}

return req, dictionary.ErrorMessage{}
}

// Request will write the request to the logs before and after calling the handler
func Request(h httprouter.Handle, a *Action) httprouter.Handle {
return parameters.MakeHTTPRouterParsedReq(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
Expand Down
44 changes: 44 additions & 0 deletions actions/transactions/broadcast_callback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package transactions

import (
"encoding/json"
"net/http"

"github.com/bitcoin-sv/go-broadcast-client/broadcast"
"github.com/julienschmidt/httprouter"
apirouter "github.com/mrz1836/go-api-router"
)

// broadcastCallback will handle a broadcastCallback call from the broadcast api
// Broadcast Callback godoc
// @Summary Broadcast Callback
// @Tags Transactions
// @Param transaction body broadcast.SubmittedTx true "transaction"
// @Success 200
// @Router /transaction/broadcast/callback [post]
// @Security callback-auth
func (a *Action) broadcastCallback(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
var resp *broadcast.SubmittedTx

err := json.NewDecoder(req.Body).Decode(&resp)
if err != nil {
apirouter.ReturnResponse(w, req, http.StatusExpectationFailed, err.Error())
return
}

defer func() {
if err = req.Body.Close(); err != nil {
a.Services.Logger.Err(err).Msg("failed to close request body")
}
}()

err = a.Services.Bux.UpdateTransaction(req.Context(), resp)
if err != nil {
a.Services.Logger.Err(err).Msgf("failed to update transaction - tx: %v", resp)
apirouter.ReturnResponse(w, req, http.StatusInternalServerError, "")
return
}

// Return response
apirouter.ReturnResponse(w, req, http.StatusOK, "")
}
6 changes: 5 additions & 1 deletion actions/transactions/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ type Action struct {

// RegisterRoutes register all the package specific routes
func RegisterRoutes(router *apirouter.Router, appConfig *config.AppConfig, services *config.AppServices) {

// Use the authentication middleware wrapper
a, require := actions.NewStack(appConfig, services)
require.Use(a.RequireAuthentication)
Expand All @@ -22,6 +21,10 @@ func RegisterRoutes(router *apirouter.Router, appConfig *config.AppConfig, servi
aBasic, requireBasic := actions.NewStack(appConfig, services)
requireBasic.Use(aBasic.RequireBasicAuthentication)

// Use the callback middleware wrapper
aCallback, requireCallback := actions.NewStack(appConfig, services)
requireCallback.Use(aCallback.RequireCallbackAuthentication)

// Load the actions and set the services
action := &Action{actions.Action{AppConfig: a.AppConfig, Services: a.Services}}

Expand All @@ -33,4 +36,5 @@ func RegisterRoutes(router *apirouter.Router, appConfig *config.AppConfig, servi
router.HTTPRouter.POST("/"+config.APIVersion+"/transaction", action.Request(router, require.Wrap(action.newTransaction)))
router.HTTPRouter.POST("/"+config.APIVersion+"/transaction/record", action.Request(router, require.Wrap(action.record)))
router.HTTPRouter.POST("/"+config.APIVersion+"/transaction/search", action.Request(router, requireBasic.Wrap(action.search)))
router.HTTPRouter.POST(config.BroadcastCallbackRoute, action.Request(router, requireCallback.Wrap(action.broadcastCallback)))
}
4 changes: 4 additions & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import (
// @securityDefinitions.apikey bux-auth-xpub
// @in header
// @name bux-auth-xpub

// @securityDefinitions.apikey callback-auth
// @in header
// @name authorization
func main() {
defaultLogger := logging.GetDefaultLogger()

Expand Down
11 changes: 9 additions & 2 deletions config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,17 @@ new_relic:
nodes:
# deployment id used annotating api calls in XDeployment-ID header - this value will be randomly generated if not set
_deployment_id: bux-deployment-id
callback:
callback_host: https://xyz.com
# token to authenticate callback calls - default callback token will be generated from the Admin Key
_callback_token: 44a82509
# protocol - arc/mapi (arc is default)
protocol: arc
# list of apis used for getting and broadcasting transactions
apis:
# gorillapool can be used as well
# - arc_url: https://arc.gorillapool.io
# - token: ""
# - arc_url: https://arc.gorillapool.io
# - token: ""
- arc_url: https://api.taal.com/arc/v1
token: mainnet_06770f425eb00298839a24a49cbdc02c
# use fee quotes for transaction fee calculation
Expand Down Expand Up @@ -135,3 +139,6 @@ server_config:
task_manager:
# task manager factory - memory, redis
factory: memory
# Prometheus metrics configuration
metrics:
enabled: false
Loading

0 comments on commit dd7aec2

Please sign in to comment.