From 7dacac88463af0d5e8acab04bdab8fc5beca6d82 Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Fri, 1 Sep 2023 23:10:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=95=B0=E6=8D=AE=E5=BA=93=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E5=8A=A0=E5=AF=86=E5=AD=98=E5=82=A8=20(#2146)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/repo/database.go | 46 +++++- backend/app/repo/databse_mysql.go | 55 +++++-- backend/app/service/database.go | 9 +- backend/app/service/database_mysql.go | 7 +- backend/init/hook/hook.go | 5 - backend/init/migration/migrate.go | 2 +- backend/init/migration/migrations/init.go | 185 +++++++++++++++------- backend/utils/encrypt/encrypt.go | 20 ++- 8 files changed, 241 insertions(+), 88 deletions(-) diff --git a/backend/app/repo/database.go b/backend/app/repo/database.go index f6a4adec9..462ea9022 100644 --- a/backend/app/repo/database.go +++ b/backend/app/repo/database.go @@ -2,10 +2,12 @@ package repo import ( "context" + "fmt" "strings" "github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/global" + "github.com/1Panel-dev/1Panel/backend/utils/encrypt" "gorm.io/gorm" ) @@ -34,20 +36,36 @@ func (d *DatabaseRepo) Get(opts ...DBOption) (model.Database, error) { for _, opt := range opts { db = opt(db) } - err := db.First(&database).Error - return database, err + if err := db.First(&database).Error; err != nil { + return database, err + } + pass, err := encrypt.StringDecrypt(database.Password) + if err != nil { + global.LOG.Errorf("decrypt database %s password failed, err: %v", database.Name, err) + } + database.Password = pass + return database, nil } func (d *DatabaseRepo) Page(page, size int, opts ...DBOption) (int64, []model.Database, error) { - var users []model.Database + var databases []model.Database db := global.DB.Model(&model.Database{}) for _, opt := range opts { db = opt(db) } count := int64(0) db = db.Count(&count) - err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error - return count, users, err + if err := db.Limit(size).Offset(size * (page - 1)).Find(&databases).Error; err != nil { + return count, databases, err + } + for i := 0; i < len(databases); i++ { + pass, err := encrypt.StringDecrypt(databases[i].Password) + if err != nil { + global.LOG.Errorf("decrypt database db %s password failed, err: %v", databases[i].Name, err) + } + databases[i].Password = pass + } + return count, databases, nil } func (d *DatabaseRepo) GetList(opts ...DBOption) ([]model.Database, error) { @@ -56,8 +74,17 @@ func (d *DatabaseRepo) GetList(opts ...DBOption) ([]model.Database, error) { for _, opt := range opts { db = opt(db) } - err := db.Find(&databases).Error - return databases, err + if err := db.Find(&databases).Error; err != nil { + return databases, err + } + for i := 0; i < len(databases); i++ { + pass, err := encrypt.StringDecrypt(databases[i].Password) + if err != nil { + global.LOG.Errorf("decrypt database db %s password failed, err: %v", databases[i].Name, err) + } + databases[i].Password = pass + } + return databases, nil } func (d *DatabaseRepo) WithByFrom(from string) DBOption { @@ -94,6 +121,11 @@ func (d *DatabaseRepo) WithAppInstallID(appInstallID uint) DBOption { } func (d *DatabaseRepo) Create(ctx context.Context, database *model.Database) error { + pass, err := encrypt.StringEncrypt(database.Password) + if err != nil { + return fmt.Errorf("decrypt database db %s password failed, err: %v", database.Name, err) + } + database.Password = pass return getTx(ctx).Create(database).Error } diff --git a/backend/app/repo/databse_mysql.go b/backend/app/repo/databse_mysql.go index fcbb4cc1e..47d107379 100644 --- a/backend/app/repo/databse_mysql.go +++ b/backend/app/repo/databse_mysql.go @@ -2,9 +2,11 @@ package repo import ( "context" + "fmt" "github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/global" + "github.com/1Panel-dev/1Panel/backend/utils/encrypt" "gorm.io/gorm" ) @@ -19,7 +21,6 @@ type IMysqlRepo interface { Create(ctx context.Context, mysql *model.DatabaseMysql) error Delete(ctx context.Context, opts ...DBOption) error Update(id uint, vars map[string]interface{}) error - UpdateDatabaseInfo(id uint, vars map[string]interface{}) error DeleteLocal(ctx context.Context) error } @@ -33,33 +34,64 @@ func (u *MysqlRepo) Get(opts ...DBOption) (model.DatabaseMysql, error) { for _, opt := range opts { db = opt(db) } - err := db.First(&mysql).Error + if err := db.First(&mysql).Error; err != nil { + return mysql, err + } + + pass, err := encrypt.StringDecrypt(mysql.Password) + if err != nil { + global.LOG.Errorf("decrypt database db %s password failed, err: %v", mysql.Name, err) + } + mysql.Password = pass return mysql, err } func (u *MysqlRepo) List(opts ...DBOption) ([]model.DatabaseMysql, error) { - var users []model.DatabaseMysql + var mysqls []model.DatabaseMysql db := global.DB.Model(&model.DatabaseMysql{}) for _, opt := range opts { db = opt(db) } - err := db.Find(&users).Error - return users, err + if err := db.Find(&mysqls).Error; err != nil { + return mysqls, err + } + for i := 0; i < len(mysqls); i++ { + pass, err := encrypt.StringDecrypt(mysqls[i].Password) + if err != nil { + global.LOG.Errorf("decrypt database db %s password failed, err: %v", mysqls[i].Name, err) + } + mysqls[i].Password = pass + } + return mysqls, nil } func (u *MysqlRepo) Page(page, size int, opts ...DBOption) (int64, []model.DatabaseMysql, error) { - var users []model.DatabaseMysql + var mysqls []model.DatabaseMysql db := global.DB.Model(&model.DatabaseMysql{}) for _, opt := range opts { db = opt(db) } count := int64(0) db = db.Count(&count) - err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error - return count, users, err + if err := db.Limit(size).Offset(size * (page - 1)).Find(&mysqls).Error; err != nil { + return count, mysqls, err + } + for i := 0; i < len(mysqls); i++ { + pass, err := encrypt.StringDecrypt(mysqls[i].Password) + if err != nil { + global.LOG.Errorf("decrypt database db %s password failed, err: %v", mysqls[i].Name, err) + } + mysqls[i].Password = pass + } + return count, mysqls, nil } func (u *MysqlRepo) Create(ctx context.Context, mysql *model.DatabaseMysql) error { + pass, err := encrypt.StringEncrypt(mysql.Password) + if err != nil { + return fmt.Errorf("decrypt database db %s password failed, err: %v", mysql.Name, err) + } + mysql.Password = pass return getTx(ctx).Create(mysql).Error } @@ -75,13 +107,6 @@ func (u *MysqlRepo) Update(id uint, vars map[string]interface{}) error { return global.DB.Model(&model.DatabaseMysql{}).Where("id = ?", id).Updates(vars).Error } -func (u *MysqlRepo) UpdateDatabaseInfo(id uint, vars map[string]interface{}) error { - if err := global.DB.Model(&model.AppInstall{}).Where("id = ?", id).Updates(vars).Error; err != nil { - return err - } - return nil -} - func (u *MysqlRepo) WithByMysqlName(mysqlName string) DBOption { return func(g *gorm.DB) *gorm.DB { return g.Where("mysql_name = ?", mysqlName) diff --git a/backend/app/service/database.go b/backend/app/service/database.go index a9d3f201a..8dd711e80 100644 --- a/backend/app/service/database.go +++ b/backend/app/service/database.go @@ -2,9 +2,11 @@ package service import ( "context" + "fmt" "github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/constant" + "github.com/1Panel-dev/1Panel/backend/utils/encrypt" "github.com/1Panel-dev/1Panel/backend/utils/mysql" "github.com/1Panel-dev/1Panel/backend/utils/mysql/client" "github.com/jinzhu/copier" @@ -136,12 +138,17 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error { return err } + pass, err := encrypt.StringEncrypt(req.Password) + if err != nil { + return fmt.Errorf("decrypt database password failed, err: %v", err) + } + upMap := make(map[string]interface{}) upMap["version"] = req.Version upMap["address"] = req.Address upMap["port"] = req.Port upMap["username"] = req.Username - upMap["password"] = req.Password + upMap["password"] = pass upMap["description"] = req.Description return databaseRepo.Update(req.ID, upMap) } diff --git a/backend/app/service/database_mysql.go b/backend/app/service/database_mysql.go index 9caa77c6f..8efffbe23 100644 --- a/backend/app/service/database_mysql.go +++ b/backend/app/service/database_mysql.go @@ -21,6 +21,7 @@ import ( "github.com/1Panel-dev/1Panel/backend/utils/cmd" "github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/1Panel-dev/1Panel/backend/utils/compose" + "github.com/1Panel-dev/1Panel/backend/utils/encrypt" "github.com/1Panel-dev/1Panel/backend/utils/mysql" "github.com/1Panel-dev/1Panel/backend/utils/mysql/client" _ "github.com/go-sql-driver/mysql" @@ -301,7 +302,11 @@ func (u *MysqlService) ChangePassword(req dto.ChangeDBInfo) error { } } global.LOG.Info("excute password change sql successful") - _ = mysqlRepo.Update(mysqlData.ID, map[string]interface{}{"password": req.Value}) + pass, err := encrypt.StringEncrypt(req.Value) + if err != nil { + return fmt.Errorf("decrypt database db password failed, err: %v", err) + } + _ = mysqlRepo.Update(mysqlData.ID, map[string]interface{}{"password": pass}) return nil } diff --git a/backend/init/hook/hook.go b/backend/init/hook/hook.go index 1baa76350..1bbf35dbc 100644 --- a/backend/init/hook/hook.go +++ b/backend/init/hook/hook.go @@ -18,11 +18,6 @@ func Init() { global.LOG.Errorf("load service port from setting failed, err: %v", err) } global.CONF.System.Port = portSetting.Value - encryptSetting, err := settingRepo.Get(settingRepo.WithByKey("EncryptKey")) - if err != nil { - global.LOG.Errorf("load service encrypt key from setting failed, err: %v", err) - } - global.CONF.System.EncryptKey = encryptSetting.Value sslSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL")) if err != nil { global.LOG.Errorf("load service ssl from setting failed, err: %v", err) diff --git a/backend/init/migration/migrate.go b/backend/init/migration/migrate.go index f006923d6..365094f71 100644 --- a/backend/init/migration/migrate.go +++ b/backend/init/migration/migrate.go @@ -39,7 +39,7 @@ func Init() { migrations.UpdateRedisParam, migrations.UpdateCronjobWithDb, migrations.AddTableFirewall, - migrations.AddMariaDB, + migrations.AddDatabases, migrations.UpdateDatabase, migrations.UpdateAppInstallResource, }) diff --git a/backend/init/migration/migrations/init.go b/backend/init/migration/migrations/init.go index f544ded1b..699c66e81 100644 --- a/backend/init/migration/migrations/init.go +++ b/backend/init/migration/migrations/init.go @@ -8,6 +8,7 @@ import ( "time" "github.com/1Panel-dev/1Panel/backend/app/model" + "github.com/1Panel-dev/1Panel/backend/app/repo" "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/utils/common" @@ -582,52 +583,97 @@ var AddTableFirewall = &gormigrate.Migration{ }, } -var AddMariaDB = &gormigrate.Migration{ - ID: "20230828-add-mariadb", +var AddDatabases = &gormigrate.Migration{ + ID: "20230831-add-databases", Migrate: func(tx *gorm.DB) error { - var ( - app model.App - appInstall model.AppInstall - ) - if err := tx.AutoMigrate(&model.Database{}); err != nil { - return err - } - if err := global.DB.Where("key = ?", "mariadb").First(&app).Error; err != nil { - return nil - } - if err := global.DB.Where("app_id = ?", app.ID).First(&appInstall).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return nil + installRepo := repo.NewIAppInstallRepo() + mariadbInfo, err := installRepo.LoadBaseInfo("mariadb", "") + if err == nil { + if err := tx.Create(&model.Database{ + AppInstallID: mariadbInfo.ID, + Name: mariadbInfo.Name, + Type: "mariadb", + Version: mariadbInfo.Version, + From: "local", + Address: mariadbInfo.ServiceName, + Port: uint(mariadbInfo.Port), + Username: "root", + Password: mariadbInfo.Password, + }).Error; err != nil { + return err } - return err } - envMap := make(map[string]interface{}) - if err := json.Unmarshal([]byte(appInstall.Env), &envMap); err != nil { - return err + redisInfo, err := installRepo.LoadBaseInfo("redis", "") + if err == nil { + if err := tx.Create(&model.Database{ + AppInstallID: redisInfo.ID, + Name: redisInfo.Name, + Type: "mariadb", + Version: redisInfo.Version, + From: "local", + Address: redisInfo.ServiceName, + Port: uint(redisInfo.Port), + Username: "root", + Password: redisInfo.Password, + }).Error; err != nil { + return err + } } - password, ok := envMap["PANEL_DB_ROOT_PASSWORD"].(string) - if !ok { - return errors.New("error password in app env") + pgInfo, err := installRepo.LoadBaseInfo("postgresql", "") + if err == nil { + if err := tx.Create(&model.Database{ + AppInstallID: pgInfo.ID, + Name: pgInfo.Name, + Type: "mariadb", + Version: pgInfo.Version, + From: "local", + Address: pgInfo.ServiceName, + Port: uint(pgInfo.Port), + Username: "root", + Password: pgInfo.Password, + }).Error; err != nil { + return err + } } - if err := tx.Create(&model.Database{ - AppInstallID: appInstall.ID, - Name: appInstall.Name, - Type: "mariadb", - Version: appInstall.Version, - From: "local", - Address: appInstall.ServiceName, - Port: uint(appInstall.HttpPort), - Username: "root", - Password: password, - }).Error; err != nil { - return err + mongodbInfo, err := installRepo.LoadBaseInfo("mongodb", "") + if err == nil { + if err := tx.Create(&model.Database{ + AppInstallID: mongodbInfo.ID, + Name: mongodbInfo.Name, + Type: "mariadb", + Version: mongodbInfo.Version, + From: "local", + Address: mongodbInfo.ServiceName, + Port: uint(mongodbInfo.Port), + Username: "root", + Password: mongodbInfo.Password, + }).Error; err != nil { + return err + } } + memcachedInfo, err := installRepo.LoadBaseInfo("memcached", "") + if err == nil { + if err := tx.Create(&model.Database{ + AppInstallID: memcachedInfo.ID, + Name: memcachedInfo.Name, + Type: "mariadb", + Version: memcachedInfo.Version, + From: "local", + Address: memcachedInfo.ServiceName, + Port: uint(memcachedInfo.Port), + Username: "root", + Password: memcachedInfo.Password, + }).Error; err != nil { + return err + } + } + return nil }, } var UpdateDatabase = &gormigrate.Migration{ - ID: "20230829-update-database", + ID: "20230831-update-database", Migrate: func(tx *gorm.DB) error { if err := global.DB.Model(&model.DatabaseMysql{}).Where("`from` != ?", "local").Updates(map[string]interface{}{ "from": "remote", @@ -635,30 +681,59 @@ var UpdateDatabase = &gormigrate.Migration{ return err } - var ( - appMysql model.App - appInstallMysql model.AppInstall - localDatabase model.Database - ) - _ = global.DB.Where("name = ? AND address = ?", "local", "127.0.0.1").First(&localDatabase).Error - if localDatabase.ID == 0 { + var datas []model.Database + if err := global.DB.Find(&datas).Error; err != nil { return nil } - if err := global.DB.Where("key = ?", "mysql").First(&appMysql).Error; err != nil { - return nil - } - if err := global.DB.Where("app_id = ?", appMysql.ID).First(&appInstallMysql).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return nil + for _, data := range datas { + if data.Name == "local" && data.Address == "127.0.0.1" && data.Type == "mysql" { + installRepo := repo.NewIAppInstallRepo() + mysqlInfo, err := installRepo.LoadBaseInfo("mysql", "") + if err != nil { + continue + } + pass, err := encrypt.StringEncrypt(data.Password) + if err != nil { + global.LOG.Errorf("encrypt database %s password failed, err: %v", data.Name, err) + continue + } + if err := global.DB.Model(&model.Database{}).Where("id = ?", data.ID).Updates(map[string]interface{}{ + "app_install_id": mysqlInfo.ID, + "name": mysqlInfo.Name, + "password": pass, + "address": mysqlInfo.ServiceName, + }).Error; err != nil { + global.LOG.Errorf("updata database %s info failed, err: %v", data.Name, err) + } + } else { + pass, err := encrypt.StringEncrypt(data.Password) + if err != nil { + global.LOG.Errorf("encrypt database %s password failed, err: %v", data.Name, err) + continue + } + if err := global.DB.Model(&model.Database{}).Where("id = ?", data.ID).Updates(map[string]interface{}{ + "password": pass, + }).Error; err != nil { + global.LOG.Errorf("updata database %s info failed, err: %v", data.Name, err) + } } - return err } - if err := global.DB.Model(&model.Database{}).Where("id = ?", localDatabase.ID).Updates(map[string]interface{}{ - "app_install_id": appInstallMysql.ID, - "name": appInstallMysql.Name, - "address": appInstallMysql.ServiceName, - }).Error; err != nil { - return err + + var mysqls []model.DatabaseMysql + if err := global.DB.Find(&mysqls).Error; err != nil { + return nil + } + for _, data := range mysqls { + pass, err := encrypt.StringEncrypt(data.Password) + if err != nil { + global.LOG.Errorf("encrypt database db %s password failed, err: %v", data.Name, err) + continue + } + if err := global.DB.Model(&model.DatabaseMysql{}).Where("id = ?", data.ID).Updates(map[string]interface{}{ + "password": pass, + }).Error; err != nil { + global.LOG.Errorf("updata database db %s info failed, err: %v", data.Name, err) + } } return nil }, diff --git a/backend/utils/encrypt/encrypt.go b/backend/utils/encrypt/encrypt.go index 9106ce88d..fe4a35ca7 100644 --- a/backend/utils/encrypt/encrypt.go +++ b/backend/utils/encrypt/encrypt.go @@ -8,16 +8,23 @@ import ( "crypto/rand" "encoding/base64" "encoding/hex" - "errors" "fmt" "io" + "github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/global" ) func StringEncrypt(text string) (string, error) { if len(text) == 0 { - return "", errors.New("it is not possible to encrypt an empty string.") + return "", nil + } + if len(global.CONF.System.EncryptKey) == 0 { + var encryptSetting model.Setting + if err := global.DB.Where("key = ?", "EncryptKey").First(&encryptSetting).Error; err != nil { + return "", err + } + global.CONF.System.EncryptKey = encryptSetting.Value } key := global.CONF.System.EncryptKey pass := []byte(text) @@ -31,7 +38,14 @@ func StringEncrypt(text string) (string, error) { func StringDecrypt(text string) (string, error) { if len(text) == 0 { - return "", errors.New("it is not possible to decrypt an empty string.") + return "", nil + } + if len(global.CONF.System.EncryptKey) == 0 { + var encryptSetting model.Setting + if err := global.DB.Where("key = ?", "EncryptKey").First(&encryptSetting).Error; err != nil { + return "", err + } + global.CONF.System.EncryptKey = encryptSetting.Value } key := global.CONF.System.EncryptKey bytesPass, err := base64.StdEncoding.DecodeString(text)