Skip to content

Commit aaf6d30

Browse files
committed
refactor node registrar
1 parent 90ca89a commit aaf6d30

File tree

9 files changed

+139
-107
lines changed

9 files changed

+139
-107
lines changed

node-registrar/cmds/main.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ import (
55
"fmt"
66
"net"
77
"os"
8-
"os/signal"
98
"strings"
10-
"syscall"
119

1210
"github.com/pkg/errors"
1311
"github.com/rs/zerolog"
@@ -57,7 +55,7 @@ func Run() error {
5755
flag.UintVar(&f.serverPort, "server-port", 8080, "server port")
5856
flag.StringVar(&f.domain, "domain", "", "domain on which the server will be served")
5957
flag.StringVar(&f.network, "network", "dev", "the registrar network")
60-
flag.Uint64Var(&f.adminTwinID, "admin-twin-id", 0, "admin twin ID")
58+
flag.Uint64Var(&f.adminTwinID, "admin-twin-id", 1, "admin twin ID")
6159

6260
flag.Parse()
6361
f.SqlLogLevel = logger.LogLevel(sqlLogLevel)
@@ -71,11 +69,11 @@ func Run() error {
7169
return err
7270
}
7371

74-
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
75-
zerolog.SetGlobalLevel(zerolog.InfoLevel)
72+
logLevel := zerolog.InfoLevel
7673
if f.debug {
77-
zerolog.SetGlobalLevel(zerolog.DebugLevel)
74+
logLevel = zerolog.DebugLevel
7875
}
76+
log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).Level(logLevel).With().Timestamp().Logger()
7977

8078
db, err := db.NewDB(f.Config)
8179
if err != nil {
@@ -89,17 +87,11 @@ func Run() error {
8987
}
9088
}()
9189

92-
s, err := server.NewServer(db, f.network, f.adminTwinID)
93-
if err != nil {
94-
return errors.Wrap(err, "failed to start gin server")
95-
}
96-
97-
quit := make(chan os.Signal, 1)
98-
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
90+
s := server.NewServer(db, f.network, f.adminTwinID)
9991

10092
log.Info().Msgf("server is running on port :%d", f.serverPort)
10193

102-
err = s.Run(quit, fmt.Sprintf("%s:%d", f.domain, f.serverPort))
94+
err = s.Run(fmt.Sprintf("%s:%d", f.domain, f.serverPort))
10395
if err != nil {
10496
return errors.Wrap(err, "failed to run gin server")
10597
}
@@ -115,6 +107,14 @@ func (f flags) validate() error {
115107
if strings.TrimSpace(f.domain) == "" {
116108
return errors.New("invalid domain name, domain name should not be empty")
117109
}
110+
111+
if f.SqlLogLevel < 1 || f.SqlLogLevel > 4 {
112+
return errors.Errorf("invalid sql log level %d, sql log level should be in the range 1-4", f.SqlLogLevel)
113+
}
114+
if f.adminTwinID == 0 {
115+
return errors.Errorf("invalid admin twin id %d, admin twin id should not be 0", f.adminTwinID)
116+
}
117+
118118
if _, err := net.LookupHost(f.domain); err != nil {
119119
return errors.Wrapf(err, "invalid domain %s", f.domain)
120120
}

node-registrar/pkg/db/db.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,24 +90,24 @@ func (c Config) Validate() error {
9090
}
9191

9292
if net.ParseIP(c.PostgresHost) == nil {
93-
if _, err := net.LookupHost(c.PostgresHost); err == nil {
93+
if _, err := net.LookupHost(c.PostgresHost); err != nil {
9494
return errors.Wrapf(err, "invalid postgres host %s, failed to parse or lookup host", c.PostgresHost)
9595
}
9696
}
9797

98-
if c.PostgresPort < 1 && c.PostgresPort > 65535 {
98+
if c.PostgresPort < 1 || c.PostgresPort > 65535 {
9999
return errors.Errorf("invalid postgres port %d, postgres port should be in the valid port range 1–65535", c.PostgresPort)
100100
}
101101

102-
if strings.TrimSpace(c.DBName) == "" {
102+
if len(strings.TrimSpace(c.DBName)) == 0 {
103103
return errors.New("invalid database name, database name should not be empty")
104104
}
105105

106-
if strings.TrimSpace(c.PostgresUser) == "" {
106+
if len(strings.TrimSpace(c.PostgresUser)) == 0 {
107107
return errors.New("invalid postgres user, postgres user should not be empty")
108108
}
109109

110-
if strings.TrimSpace(c.PostgresPassword) == "" {
110+
if len(strings.TrimSpace(c.PostgresPassword)) == 0 {
111111
return errors.New("invalid postgres password, postgres password should not be empty")
112112
}
113113

node-registrar/pkg/db/models.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ type Account struct {
2323
type Farm struct {
2424
FarmID uint64 `gorm:"primaryKey;autoIncrement" json:"farm_id"`
2525
FarmName string `gorm:"size:40;not null;unique;check:farm_name <> ''" json:"farm_name" binding:"alphanum,required"`
26-
TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0"` // Farmer account reference
26+
TwinID uint64 `json:"twin_id" binding:"required" gorm:"not null;check:twin_id > 0"` // Farmer account reference
2727
StellarAddress string `json:"stellar_address" binding:"max=56,startswith=G,len=56,alphanum,uppercase"`
2828
Dedicated bool `json:"dedicated"`
2929
CreatedAt time.Time `json:"created_at"`

node-registrar/pkg/server/handlers.go

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,10 @@ import (
1111

1212
"github.com/gin-gonic/gin"
1313
"github.com/lib/pq"
14-
"github.com/rs/zerolog/log"
1514
"github.com/threefoldtech/tfgrid4-sdk-go/node-registrar/pkg/db"
1615
)
1716

1817
const (
19-
PubKeySize = 32
2018
MaxTimestampDelta = 2 * time.Second
2119
)
2220

@@ -78,13 +76,14 @@ func (s Server) getFarmHandler(c *gin.Context) {
7876

7977
farm, err := s.db.GetFarm(id)
8078
if err != nil {
81-
status := http.StatusBadRequest
79+
status := http.StatusInternalServerError
8280

8381
if errors.Is(err, db.ErrRecordNotFound) {
8482
status = http.StatusNotFound
8583
}
8684

8785
c.JSON(status, gin.H{"error": err.Error()})
86+
return
8887
}
8988

9089
c.JSON(http.StatusOK, farm)
@@ -117,7 +116,7 @@ func (s Server) createFarmHandler(c *gin.Context) {
117116

118117
farmID, err := s.db.CreateFarm(farm)
119118
if err != nil {
120-
status := http.StatusBadRequest
119+
status := http.StatusInternalServerError
121120

122121
if errors.Is(err, db.ErrRecordAlreadyExists) {
123122
status = http.StatusConflict
@@ -148,7 +147,7 @@ type UpdateFarmRequest struct {
148147
// @Failure 401 {object} map[string]any "Unauthorized"
149148
// @Failure 404 {object} map[string]any "Farm not found"
150149
// @Router /farms/{farm_id} [patch]
151-
func (s Server) updateFarmsHandler(c *gin.Context) {
150+
func (s Server) updateFarmHandler(c *gin.Context) {
152151
var req UpdateFarmRequest
153152
farmID := c.Param("farm_id")
154153

@@ -186,7 +185,7 @@ func (s Server) updateFarmsHandler(c *gin.Context) {
186185
(len(req.StellarAddress) != 0 && existingFarm.StellarAddress != req.StellarAddress) {
187186
err = s.db.UpdateFarm(id, req.FarmName, req.StellarAddress)
188187
if err != nil {
189-
status := http.StatusBadRequest
188+
status := http.StatusInternalServerError
190189

191190
if errors.Is(err, db.ErrRecordNotFound) {
192191
status = http.StatusNotFound
@@ -229,7 +228,7 @@ func (s Server) listNodesHandler(c *gin.Context) {
229228

230229
nodes, err := s.db.ListNodes(filter, limit)
231230
if err != nil {
232-
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
231+
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
233232
return
234233
}
235234

@@ -262,7 +261,7 @@ func (s Server) getNodeHandler(c *gin.Context) {
262261
return
263262
}
264263

265-
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
264+
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
266265
return
267266
}
268267

@@ -319,7 +318,7 @@ func (s Server) registerNodeHandler(c *gin.Context) {
319318

320319
nodeID, err := s.db.RegisterNode(node)
321320
if err != nil {
322-
status := http.StatusBadRequest
321+
status := http.StatusInternalServerError
323322

324323
if errors.Is(err, db.ErrRecordAlreadyExists) {
325324
status = http.StatusConflict
@@ -377,13 +376,11 @@ func (s *Server) updateNodeHandler(c *gin.Context) {
377376
return
378377
}
379378

380-
log.Info().Any("req is", c.Request.Body)
381379
var req UpdateNodeRequest
382380
if err := c.ShouldBindJSON(&req); err != nil {
383381
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
384382
return
385383
}
386-
log.Debug().Any("req", req).Send()
387384

388385
updatedNode := db.Node{
389386
FarmID: req.FarmID,
@@ -541,11 +538,13 @@ func (s *Server) createAccountHandler(c *gin.Context) {
541538
publicKeyBytes, err := base64.StdEncoding.DecodeString(req.PublicKey)
542539
if err != nil {
543540
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid public key format"})
541+
return
544542
}
545543
// Decode signature from base64
546544
signatureBytes, err := base64.StdEncoding.DecodeString(req.Signature)
547545
if err != nil {
548546
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("invalid signature format: %v", err)})
547+
return
549548
}
550549
// Verify signature of the challenge
551550
err = verifySignature(publicKeyBytes, challenge, signatureBytes)
@@ -571,13 +570,6 @@ func (s *Server) createAccountHandler(c *gin.Context) {
571570
c.JSON(http.StatusCreated, account)
572571
}
573572

574-
/* // verifySignature verifies an ED25519 signature
575-
func verifySignature(publicKey, chalange, signature []byte) (bool, error) {
576-
577-
// Verify the signature
578-
return ed25519.Verify(publicKey, chalange, signature), nil
579-
} */
580-
581573
type UpdateAccountRequest struct {
582574
Relays pq.StringArray `json:"relays"`
583575
RMBEncKey string `json:"rmb_enc_key"`
@@ -668,7 +660,7 @@ func (s *Server) getAccountHandler(c *gin.Context) {
668660

669661
account, err := s.db.GetAccount(twinID)
670662
if err != nil {
671-
if err == db.ErrRecordNotFound {
663+
if errors.Is(err, db.ErrRecordNotFound) {
672664
c.JSON(http.StatusNotFound, gin.H{"error": "account not found"})
673665
return
674666
}
@@ -683,14 +675,13 @@ func (s *Server) getAccountHandler(c *gin.Context) {
683675
if publicKeyParam != "" {
684676
account, err := s.db.GetAccountByPublicKey(publicKeyParam)
685677
if err != nil {
686-
if err == db.ErrRecordNotFound {
678+
if errors.Is(err, db.ErrRecordNotFound) {
687679
c.JSON(http.StatusNotFound, gin.H{"error": "account not found"})
688680
return
689681
}
690682
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get account"})
691683
return
692684
}
693-
log.Info().Any("account", account).Send()
694685
c.JSON(http.StatusOK, account)
695686
return
696687
}
@@ -759,7 +750,7 @@ func (s *Server) getZOSVersionHandler(c *gin.Context) {
759750
c.JSON(http.StatusOK, version)
760751
}
761752

762-
// Helper function to validate public key format
753+
// Helper function to validate public key length
763754
func isValidPublicKey(publicKeyBase64 string) bool {
764755
publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64)
765756
if err != nil {
@@ -771,8 +762,8 @@ func isValidPublicKey(publicKeyBase64 string) bool {
771762
// Helper function to ensure the request is from the owner
772763
func ensureOwner(c *gin.Context, twinID uint64) {
773764
// Retrieve twinID set by the authMiddleware
774-
authTwinID, exists := c.Get("twinID")
775-
if !exists {
765+
authTwinID := c.Request.Context().Value(twinIDKey{})
766+
if authTwinID == nil {
776767
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
777768
return
778769
}

node-registrar/pkg/server/middlewares.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package server
22

33
import (
4+
"context"
45
"encoding/base64"
56
"errors"
67
"fmt"
@@ -10,12 +11,16 @@ import (
1011
"time"
1112

1213
"github.com/gin-gonic/gin"
14+
"github.com/rs/zerolog/log"
1315
"github.com/threefoldtech/tfgrid4-sdk-go/node-registrar/pkg/db"
1416
)
1517

18+
// twinKeyID is where the twin key is stored
19+
type twinIDKey struct{}
20+
1621
const (
1722
AuthHeader = "X-Auth"
18-
ChallengeValidity = 5 * time.Minute
23+
ChallengeValidity = 1 * time.Minute
1924
)
2025

2126
// AuthMiddleware is a middleware function that authenticates incoming requests based on the X-Auth header.
@@ -26,7 +31,6 @@ const (
2631
// header format `Challenge:Signature`
2732
// - chalange format: base64(message) where the message is `timestampStr:twinIDStr`
2833
// - signature format: base64(ed25519_or_sr22519_signature)
29-
// TODO: do we need to support both? Maybe if only ed25519 needed we can rely on crypto pkg instead of using go-subkey
3034
func (s *Server) AuthMiddleware() gin.HandlerFunc {
3135
return func(c *gin.Context) {
3236
// Extract and validate headers
@@ -47,6 +51,7 @@ func (s *Server) AuthMiddleware() gin.HandlerFunc {
4751
// Decode and validate challenge
4852
challenge, err := base64.StdEncoding.DecodeString(challengeB64)
4953
if err != nil {
54+
log.Debug().Err(err).Msg("failed to deconde challenge")
5055
abortWithError(c, http.StatusBadRequest, "Invalid challenge encoding")
5156
return
5257
}
@@ -62,6 +67,7 @@ func (s *Server) AuthMiddleware() gin.HandlerFunc {
6267
// Validate timestamp
6368
timestamp, err := strconv.ParseInt(timestampStr, 10, 64)
6469
if err != nil {
70+
log.Debug().Err(err).Msg("invalid timestamp")
6571
abortWithError(c, http.StatusBadRequest, "Invalid timestamp")
6672
return
6773
}
@@ -73,36 +79,41 @@ func (s *Server) AuthMiddleware() gin.HandlerFunc {
7379

7480
twinID, err := strconv.ParseUint(twinIDStr, 10, 64)
7581
if err != nil {
82+
log.Debug().Err(err).Msg("invalid twin id format")
7683
abortWithError(c, http.StatusBadRequest, "Invalid twin ID format")
7784
return
7885
}
7986

8087
account, err := s.db.GetAccount(twinID)
8188
if err != nil {
89+
log.Debug().Err(err).Uint64("twinID", twinID).Msg("failed to get account")
8290
handleDatabaseError(c, err)
8391
return
8492
}
8593

8694
storedPK, err := base64.StdEncoding.DecodeString(account.PublicKey)
8795
if err != nil {
96+
log.Debug().Err(err).Msg("failed to get invalid stored public key")
8897
abortWithError(c, http.StatusBadRequest, fmt.Sprintf("invalid stored public key: %v", err))
8998
return
9099
}
91100

92101
sig, err := base64.StdEncoding.DecodeString(signatureB64)
93102
if err != nil {
103+
log.Debug().Err(err).Msg("invalid signature encoding")
94104
abortWithError(c, http.StatusBadRequest, "Invalid signature encoding")
95105
return
96106
}
97107

98108
// Verify signature (supports both ED25519 and SR25519)
99109
if err := verifySignature(storedPK, challenge, sig); err != nil {
110+
log.Debug().Err(err).Msg("signature verification failed")
100111
abortWithError(c, http.StatusUnauthorized, fmt.Sprintf("Signature verification failed: %v", err))
101112
return
102113
}
103114

104115
// Store verified twin ID in context, must be checked form the handlers to ensure altred resources belongs to same user
105-
c.Set("twinID", twinID)
116+
c.Request = c.Request.WithContext(context.WithValue(c.Request.Context(), twinIDKey{}, twinID))
106117
c.Next()
107118
}
108119
}

0 commit comments

Comments
 (0)