Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea/
26 changes: 24 additions & 2 deletions backend/app/admin_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,23 @@ func (h *Handler) ListUsersHandler(c *gin.Context) {
go func(user models.User) {
defer wg.Done()
defer func() { <-balanceConcurrencyLimiter }()
if len(user.Mnemonic) == 0 || len(user.AccountAddress) == 0 {
mu.Lock()
usersWithBalance = append(usersWithBalance, UserResponse{User: user, Balance: 0})
mu.Unlock()
return
}

decryptedMnemonic, err := h.cryptoManager.Decrypt(user.Mnemonic, user.AccountAddress)
if err != nil {
logger.GetLogger().Error().Err(err).Int("user_id", user.ID).Msg("failed to decrypt user mnemonic")
mu.Lock()
multiErr = multierror.Append(multiErr, fmt.Errorf("user %d: failed to decrypt mnemonic", user.ID))
mu.Unlock()
return
}

balance, err := internal.GetUserBalanceUSDMillicent(h.substrateClient, user.Mnemonic)
balance, err := internal.GetUserBalanceUSDMillicent(h.substrateClient, decryptedMnemonic)
if err != nil {
logger.GetLogger().Error().Err(err).Int("user_id", user.ID).Msg("failed to get user balance")
mu.Lock()
Expand Down Expand Up @@ -351,10 +366,17 @@ func (h *Handler) CreditUserHandler(c *gin.Context) {
return
}

decryptedMnemonic, err := h.cryptoManager.Decrypt(user.Mnemonic, user.AccountAddress)
if err != nil {
logger.GetLogger().Error().Err(err).Send()
InternalServerError(c)
return
}

wf.State = map[string]interface{}{
"user_id": user.ID,
"amount": internal.FromUSDToUSDMillicent(request.AmountUSD),
"mnemonic": user.Mnemonic,
"mnemonic": decryptedMnemonic,
"username": user.Username,
"transfer_mode": models.AdminCreditMode,
"admin_id": adminID,
Expand Down
11 changes: 10 additions & 1 deletion backend/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ type App struct {

// NewApp create new instance of the app with all configs
func NewApp(ctx context.Context, config internal.Configuration) (*App, error) {

cryptoMgr := internal.NewCryptoManager(config)

// Disable gin's default logging since we're using zerolog
gin.DisableConsoleColor()
gin.SetMode(gin.ReleaseMode)
Expand Down Expand Up @@ -77,6 +80,11 @@ func NewApp(ctx context.Context, config internal.Configuration) (*App, error) {
return nil, fmt.Errorf("failed to create user storage: %w", err)
}

if err := cryptoMgr.EnsureMnemonicsEncrypted(ctx, db); err != nil {
logger.GetLogger().Error().Err(err).Msg("mnemonic encryption initializer failed")
return nil, fmt.Errorf("mnemonic encryption initializer failed: %w", err)
}

gridProxy := proxy.NewRetryingClient(proxy.NewClient(config.GridProxyURL))

manager := substrate.NewManager(config.TFChainURL)
Expand Down Expand Up @@ -207,7 +215,7 @@ func NewApp(ctx context.Context, config internal.Configuration) (*App, error) {
handler := NewHandler(tokenHandler, db, config, mailService, gridProxy,
substrateClient, graphqlClient, firesquidClient, redisClient,
sseManager, ewfEngine, config.SystemAccount.Network, sshPublicKey,
systemIdentity, kycClient, sponsorKeyPair, sponsorAddress, metrics, notificationService, gridClient)
systemIdentity, kycClient, sponsorKeyPair, sponsorAddress, metrics, notificationService, gridClient, cryptoMgr)

app := &App{
router: router,
Expand Down Expand Up @@ -235,6 +243,7 @@ func NewApp(ctx context.Context, config internal.Configuration) (*App, error) {
app.metrics,
app.notificationService,
gridProxy,
cryptoMgr,
)

app.registerHandlers()
Expand Down
10 changes: 8 additions & 2 deletions backend/app/balance_monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
"kubecloud/models"
"time"

"github.com/pkg/errors"
"kubecloud/internal/logger"

"github.com/pkg/errors"
)

func (h *Handler) MonitorSystemBalanceAndHandleSettlement() {
Expand Down Expand Up @@ -77,7 +78,12 @@ func (h *Handler) transferTFTsToUser(userID, recordID int, amountToTransfer uint
return errors.Wrapf(err, "failed to get user for pending record ID %d", recordID)
}

err = internal.TransferTFTs(h.substrateClient, amountToTransfer, user.Mnemonic, h.systemIdentity)
decryptedMnemonic, err := h.cryptoManager.Decrypt(user.Mnemonic, user.AccountAddress)
if err != nil {
return errors.Wrapf(err, "failed to decrypt user mnemonic for pending record ID %d", recordID)
}

err = internal.TransferTFTs(h.substrateClient, amountToTransfer, decryptedMnemonic, h.systemIdentity)
if err != nil {
return errors.Wrapf(err, "Failed to transfer TFTs for pending record ID %d", recordID)
}
Expand Down
12 changes: 10 additions & 2 deletions backend/app/debt_tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import (
"kubecloud/internal"
"time"

"kubecloud/internal/logger"

substrate "github.com/threefoldtech/tfchain/clients/tfchain-client-go"
"github.com/threefoldtech/tfgrid-sdk-go/grid-client/calculator"
"github.com/threefoldtech/tfgrid-sdk-go/grid-client/deployer"
"kubecloud/internal/logger"
)

func (h *Handler) TrackUserDebt(gridClient deployer.TFPluginClient) {
Expand All @@ -33,8 +34,15 @@ func (h *Handler) updateUserDebt(gridClient deployer.TFPluginClient) error {
logger.GetLogger().Error().Err(err).Send()
continue
}

decryptedMnemonic, err := h.cryptoManager.Decrypt(user.Mnemonic, user.AccountAddress)
if err != nil {
logger.GetLogger().Error().Err(err).Send()
continue
}

// Create identity from mnemonic
identity, err := substrate.NewIdentityFromSr25519Phrase(user.Mnemonic)
identity, err := substrate.NewIdentityFromSr25519Phrase(decryptedMnemonic)
if err != nil {
logger.GetLogger().Error().Err(err).Send()
continue
Expand Down
41 changes: 37 additions & 4 deletions backend/app/deployment_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,23 @@ func (h *Handler) HandleGetKubeconfig(c *gin.Context) {
return
}

if cluster.Kubeconfig != "" {
c.JSON(http.StatusOK, gin.H{"kubeconfig": cluster.Kubeconfig})
if len(cluster.Kubeconfig) > 0 {

user, err := h.db.GetUserByID(userID)
if err != nil {
logger.GetLogger().Error().Err(err).Int("user_id", userID).Msg("Failed to get user for kubeconfig decryption")
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve user information"})
return
}

decryptedKubeconfig, err := h.cryptoManager.Decrypt(cluster.Kubeconfig, user.AccountAddress)
if err != nil {
logger.GetLogger().Error().Err(err).Int("cluster_id", cluster.ID).Msg("Failed to decrypt kubeconfig")
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to decrypt kubeconfig"})
return
}

c.JSON(http.StatusOK, gin.H{"kubeconfig": decryptedKubeconfig})
return
}

Expand Down Expand Up @@ -255,7 +270,20 @@ func (h *Handler) HandleGetKubeconfig(c *gin.Context) {
return
}

cluster.Kubeconfig = kubeconfig
user, err := h.db.GetUserByID(cluster.UserID)
if err != nil {
logger.GetLogger().Error().Err(err).Int("user_id", cluster.UserID).Msg("Failed to get user for kubeconfig encryption")
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user for kubeconfig encryption"})
return
}
encryptedKubeconfig, err := h.cryptoManager.Encrypt(kubeconfig, user.AccountAddress)
if err != nil {
logger.GetLogger().Error().Err(err).Int("user_id", user.ID).Msg("Failed to encrypt kubeconfig")
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to encrypt kubeconfig"})
return
}

cluster.Kubeconfig = encryptedKubeconfig
if err := h.db.UpdateCluster(&cluster); err != nil {
logger.GetLogger().Error().Err(err).Int("cluster_id", cluster.ID).Msg("Failed to save kubeconfig to database")
}
Expand All @@ -274,9 +302,14 @@ func (h *Handler) getClientConfig(c *gin.Context) (statemanager.ClientConfig, er
return statemanager.ClientConfig{}, fmt.Errorf("failed to get user: %v", err)
}

decryptedMnemonic, err := h.cryptoManager.Decrypt(user.Mnemonic, user.AccountAddress)
if err != nil {
return statemanager.ClientConfig{}, fmt.Errorf("failed to decrypt user mnemonic: %w", err)
}

return statemanager.ClientConfig{
SSHPublicKey: h.sshPublicKey,
Mnemonic: user.Mnemonic,
Mnemonic: decryptedMnemonic,
UserID: userID,
Network: h.config.SystemAccount.Network,
Debug: h.config.Debug,
Expand Down
28 changes: 24 additions & 4 deletions backend/app/node_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,15 @@ func (h *Handler) ReserveNodeHandler(c *gin.Context) {
return
}
// validate user has enough balance for reserving node
usdMillicentBalance, err := internal.GetUserBalanceUSDMillicent(h.substrateClient, user.Mnemonic)

decryptedMnemonic, err := h.cryptoManager.Decrypt(user.Mnemonic, user.AccountAddress)
if err != nil {
logger.GetLogger().Error().Err(err).Send()
InternalServerError(c)
return
}

usdMillicentBalance, err := internal.GetUserBalanceUSDMillicent(h.substrateClient, decryptedMnemonic)
if err != nil {
logger.GetLogger().Error().Err(err).Send()
InternalServerError(c)
Expand All @@ -288,7 +296,7 @@ func (h *Handler) ReserveNodeHandler(c *gin.Context) {

wf.State = map[string]interface{}{
"user_id": userID,
"mnemonic": user.Mnemonic,
"mnemonic": decryptedMnemonic,
"node_id": nodeID,
"target_status": constants.NodeRented,
}
Expand Down Expand Up @@ -431,9 +439,16 @@ func (h *Handler) UnreserveNodeHandler(c *gin.Context) {
return
}

decryptedMnemonic, err := h.cryptoManager.Decrypt(user.Mnemonic, user.AccountAddress)
if err != nil {
logger.GetLogger().Error().Err(err).Send()
InternalServerError(c)
return
}

wf.State = map[string]interface{}{
"user_id": userID,
"mnemonic": user.Mnemonic,
"mnemonic": decryptedMnemonic,
"contract_id": contractID,
"node_id": userNode.NodeID,
"target_status": constants.NodeRentable,
Expand Down Expand Up @@ -537,7 +552,12 @@ func (h *Handler) getTwinIDFromUserID(userID int) (uint64, error) {
return 0, err
}

identity, err := substrate.NewIdentityFromSr25519Phrase(user.Mnemonic)
decryptedMnemonic, err := h.cryptoManager.Decrypt(user.Mnemonic, user.AccountAddress)
if err != nil {
return 0, fmt.Errorf("failed to decrypt user mnemonic: %w", err)
}

identity, err := substrate.NewIdentityFromSr25519Phrase(decryptedMnemonic)
if err != nil {
return 0, err
}
Expand Down
22 changes: 15 additions & 7 deletions backend/app/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,14 @@ func SetUp(t testing.TB) (*App, error) {
"cluster_health_check_interval_in_hours": 1,
"reserved_node_health_check_interval_in_hours": 1,
"reserved_node_health_check_timeout_in_minutes": 1,
"reserved_node_health_check_workers_num": 10
"reserved_node_health_check_workers_num": 10,
"encryption_passphrase": "test-encryption-passphrase-for-testing-only",
"encryption_salt": "test-salt-for-testing-environment",
"argon2": {
"time": 1,
"memory": 1024,
"threads": 1
}
}
`, dbPath, mnemonic, redisHost, workflowPath, privateKeyPath, publicKeyPath, notificationConfigPath)

Expand Down Expand Up @@ -181,14 +188,15 @@ func GetAuthToken(t *testing.T, app *App, id int, email, username string, isAdmi
func CreateTestUser(t *testing.T, app *App, email, username string, hashedPassword []byte, verified, admin bool, mnemonicRequired bool, code int, updatedAt time.Time) *models.User {
mnemonic := ""
sponseeAddress := ""
var encryptedBytes []byte
if !mnemonicRequired {
mnemonic = ""
// no mnemonic needed in tests; leave encryptedBytes nil
} else {
mnemonic, _, err := internal.SetupUserOnTFChain(app.handlers.substrateClient, app.config)
var err error
mnemonic, sponseeAddress, _, err = internal.SetupUserOnTFChain(app.handlers.substrateClient, app.config)
require.NoError(t, err)
sponseeKeyPair, err := internal.KeyPairFromMnemonic(mnemonic)
require.NoError(t, err)
sponseeAddress, err = internal.AccountAddressFromKeypair(sponseeKeyPair)

encryptedBytes, err = app.handlers.cryptoManager.Encrypt(mnemonic, sponseeAddress)
require.NoError(t, err)
}
user := &models.User{
Expand All @@ -199,7 +207,7 @@ func CreateTestUser(t *testing.T, app *App, email, username string, hashedPasswo
Admin: admin,
Code: code,
UpdatedAt: updatedAt,
Mnemonic: mnemonic,
Mnemonic: encryptedBytes,
AccountAddress: sponseeAddress,
}
err := app.handlers.db.RegisterUser(user)
Expand Down
Loading
Loading