Skip to content

Commit d583612

Browse files
authored
Merge pull request #38 from threefoldtech/development-refactor-registrar
refactor node registrar
2 parents f1f8fcc + ed75337 commit d583612

File tree

9 files changed

+144
-105
lines changed

9 files changed

+144
-105
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:"required,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 & 22 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
@@ -382,7 +381,6 @@ func (s *Server) updateNodeHandler(c *gin.Context) {
382381
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
383382
return
384383
}
385-
log.Debug().Any("req", req).Send()
386384

387385
updatedNode := db.Node{
388386
FarmID: req.FarmID,
@@ -540,11 +538,13 @@ func (s *Server) createAccountHandler(c *gin.Context) {
540538
publicKeyBytes, err := base64.StdEncoding.DecodeString(req.PublicKey)
541539
if err != nil {
542540
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid public key format"})
541+
return
543542
}
544543
// Decode signature from base64
545544
signatureBytes, err := base64.StdEncoding.DecodeString(req.Signature)
546545
if err != nil {
547546
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("invalid signature format: %v", err)})
547+
return
548548
}
549549
// Verify signature of the challenge
550550
err = verifySignature(publicKeyBytes, challenge, signatureBytes)
@@ -572,13 +572,6 @@ func (s *Server) createAccountHandler(c *gin.Context) {
572572
c.JSON(http.StatusCreated, account)
573573
}
574574

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

670663
account, err := s.db.GetAccount(twinID)
671664
if err != nil {
672-
if err == db.ErrRecordNotFound {
665+
if errors.Is(err, db.ErrRecordNotFound) {
673666
c.JSON(http.StatusNotFound, gin.H{"error": "account not found"})
674667
return
675668
}
@@ -684,7 +677,7 @@ func (s *Server) getAccountHandler(c *gin.Context) {
684677
if publicKeyParam != "" {
685678
account, err := s.db.GetAccountByPublicKey(publicKeyParam)
686679
if err != nil {
687-
if err == db.ErrRecordNotFound {
680+
if errors.Is(err, db.ErrRecordNotFound) {
688681
c.JSON(http.StatusNotFound, gin.H{"error": "account not found"})
689682
return
690683
}
@@ -759,7 +752,7 @@ func (s *Server) getZOSVersionHandler(c *gin.Context) {
759752
c.JSON(http.StatusOK, version)
760753
}
761754

762-
// Helper function to validate public key format
755+
// Helper function to validate public key length
763756
func isValidPublicKey(publicKeyBase64 string) bool {
764757
publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64)
765758
if err != nil {
@@ -771,8 +764,8 @@ func isValidPublicKey(publicKeyBase64 string) bool {
771764
// Helper function to ensure the request is from the owner
772765
func ensureOwner(c *gin.Context, twinID uint64) {
773766
// Retrieve twinID set by the authMiddleware
774-
authTwinID, exists := c.Get("twinID")
775-
if !exists {
767+
authTwinID := c.Request.Context().Value(twinIDKey{})
768+
if authTwinID == nil {
776769
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
777770
return
778771
}

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)