diff --git a/backend/app.yaml b/backend/app.yaml index e65cc9c93..427b29c65 100644 --- a/backend/app.yaml +++ b/backend/app.yaml @@ -3,25 +3,6 @@ system: db_type: sqlite level: debug -jwt: - header_name: Authorization - signing_key: 1panelKey - expires_time: 604800 #过期时间 - buffer_time: 86400 #缓冲时间 缓冲时间内会获取新的token刷新令牌 - issuer: 1Panel - -session: - session_name: psession - expires_time: 604800 - -captcha: - enable: true - source: "1234567890QWERTYUIOPLKJHGFDSAZXCVBNMqwertyuioplkjhgfdsazxcvbnm" - length: 4 - noise-count: 0 - img-width: 120 - img-height: 50 - mysql: path: localhost port: 3306 diff --git a/backend/app/api/v1/auth.go b/backend/app/api/v1/auth.go new file mode 100644 index 000000000..c2d4c4a07 --- /dev/null +++ b/backend/app/api/v1/auth.go @@ -0,0 +1,51 @@ +package v1 + +import ( + "github.com/1Panel-dev/1Panel/app/api/v1/helper" + "github.com/1Panel-dev/1Panel/app/dto" + "github.com/1Panel-dev/1Panel/constant" + "github.com/1Panel-dev/1Panel/global" + "github.com/1Panel-dev/1Panel/utils/captcha" + "github.com/gin-gonic/gin" +) + +type BaseApi struct{} + +func (b *BaseApi) Login(c *gin.Context) { + var req dto.Login + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := captcha.VerifyCode(req.CaptchaID, req.Captcha); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + user, err := authService.Login(c, req) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, user) +} + +func (b *BaseApi) LogOut(c *gin.Context) { + if err := authService.LogOut(c); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) Captcha(c *gin.Context) { + captcha, err := captcha.CreateCaptcha() + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + } + helper.SuccessWithData(c, captcha) +} diff --git a/backend/app/api/v1/entry.go b/backend/app/api/v1/entry.go index 31ace100e..9677aa026 100644 --- a/backend/app/api/v1/entry.go +++ b/backend/app/api/v1/entry.go @@ -9,7 +9,7 @@ type ApiGroup struct { var ApiGroupApp = new(ApiGroup) var ( - userService = service.ServiceGroupApp.UserService + authService = service.ServiceGroupApp.AuthService hostService = service.ServiceGroupApp.HostService groupService = service.ServiceGroupApp.GroupService commandService = service.ServiceGroupApp.CommandService diff --git a/backend/app/api/v1/setting.go b/backend/app/api/v1/setting.go index 9ba3146f9..7a9977928 100644 --- a/backend/app/api/v1/setting.go +++ b/backend/app/api/v1/setting.go @@ -28,7 +28,25 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) { return } - if err := settingService.Update(req.Key, req.Value); err != nil { + if err := settingService.Update(c, req.Key, req.Value); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) UpdatePassword(c *gin.Context) { + var req dto.PasswordUpdate + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + if err := settingService.UpdatePassword(c, req.OldPassword, req.NewPassword); err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return } diff --git a/backend/app/api/v1/user.go b/backend/app/api/v1/user.go deleted file mode 100644 index e67d3235b..000000000 --- a/backend/app/api/v1/user.go +++ /dev/null @@ -1,145 +0,0 @@ -package v1 - -import ( - "github.com/1Panel-dev/1Panel/app/api/v1/helper" - "github.com/1Panel-dev/1Panel/app/dto" - "github.com/1Panel-dev/1Panel/constant" - "github.com/1Panel-dev/1Panel/global" - "github.com/1Panel-dev/1Panel/utils/captcha" - "github.com/gin-gonic/gin" -) - -type BaseApi struct{} - -func (b *BaseApi) Login(c *gin.Context) { - var req dto.Login - if err := c.ShouldBindJSON(&req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - if err := global.VALID.Struct(req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - if err := captcha.VerifyCode(req.CaptchaID, req.Captcha); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) - return - } - - user, err := userService.Login(c, req) - if err != nil { - helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) - return - } - helper.SuccessWithData(c, user) -} - -func (b *BaseApi) LogOut(c *gin.Context) { - if err := userService.LogOut(c); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) - return - } - helper.SuccessWithData(c, nil) -} - -func (b *BaseApi) Captcha(c *gin.Context) { - captcha, err := captcha.CreateCaptcha() - if err != nil { - helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) - } - helper.SuccessWithData(c, captcha) -} - -func (b *BaseApi) Register(c *gin.Context) { - var req dto.UserCreate - if err := c.ShouldBindJSON(&req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - if err := global.VALID.Struct(req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - if err := userService.Register(req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) - return - } - helper.SuccessWithData(c, nil) -} - -func (b *BaseApi) PageUsers(c *gin.Context) { - var req dto.SearchWithPage - if err := c.ShouldBindJSON(&req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - - total, list, err := userService.Page(req) - if err != nil { - helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) - return - } - - helper.SuccessWithData(c, dto.PageResult{ - Items: list, - Total: total, - }) -} - -func (b *BaseApi) DeleteUser(c *gin.Context) { - var req dto.BatchDeleteReq - if err := c.ShouldBindJSON(&req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - if err := global.VALID.Struct(req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - - if err := userService.BatchDelete(req.Ids); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) - return - } - helper.SuccessWithData(c, nil) -} - -func (b *BaseApi) UpdateUser(c *gin.Context) { - var req dto.UserUpdate - if err := c.ShouldBindJSON(&req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - if err := global.VALID.Struct(req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - id, err := helper.GetParamID(c) - if err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - - upMap := make(map[string]interface{}) - upMap["email"] = req.Email - upMap["name"] = req.Name - if err := userService.Update(id, upMap); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) - return - } - helper.SuccessWithData(c, nil) -} - -func (b *BaseApi) GetUserInfo(c *gin.Context) { - id, err := helper.GetParamID(c) - if err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - user, err := userService.Get(id) - if err != nil { - helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) - return - } - helper.SuccessWithData(c, user) -} diff --git a/backend/app/dto/setting.go b/backend/app/dto/setting.go index b589e8932..4f1cf2a01 100644 --- a/backend/app/dto/setting.go +++ b/backend/app/dto/setting.go @@ -30,3 +30,8 @@ type SettingUpdate struct { Key string `json:"key" validate:"required"` Value string `json:"value"` } + +type PasswordUpdate struct { + OldPassword string `json:"oldPassword" validate:"required"` + NewPassword string `json:"newPassword" validate:"required"` +} diff --git a/backend/app/dto/user.go b/backend/app/dto/user.go index cb4c2df6f..4f631a4cb 100644 --- a/backend/app/dto/user.go +++ b/backend/app/dto/user.go @@ -1,33 +1,10 @@ package dto -import ( - "time" -) - -type UserCreate struct { - Name string `json:"name" validate:"name,required"` - Password string `json:"password" validate:"required"` - Email string `json:"email" validate:"required,email"` -} - type CaptchaResponse struct { CaptchaID string `json:"captchaID"` ImagePath string `json:"imagePath"` } -type UserUpdate struct { - ID uint `json:"id" validate:"required"` - Name string `json:"name" validate:"required"` - Email string `json:"email" validate:"required,email"` -} - -type UserInfo struct { - ID uint `json:"id"` - Name string `json:"name"` - Email string `json:"email"` - CreatedAt time.Time `json:"createdAt"` -} - type UserLoginInfo struct { Name string `json:"name"` Token string `json:"token"` diff --git a/backend/app/model/user.go b/backend/app/model/user.go deleted file mode 100644 index 210f15799..000000000 --- a/backend/app/model/user.go +++ /dev/null @@ -1,10 +0,0 @@ -package model - -import "gorm.io/gorm" - -type User struct { - gorm.Model - Name string `json:"name" gorm:"type:varchar(64);not null;"` - Password string `json:"password" gorm:"type:varchar(64)"` - Email string `json:"email" gorm:"type:varchar(64);not null;"` -} diff --git a/backend/app/repo/entry.go b/backend/app/repo/entry.go index 5bc9060de..58f4d57e5 100644 --- a/backend/app/repo/entry.go +++ b/backend/app/repo/entry.go @@ -1,7 +1,6 @@ package repo type RepoGroup struct { - UserRepo HostRepo GroupRepo CommandRepo diff --git a/backend/app/repo/setting.go b/backend/app/repo/setting.go index 27ba6aa36..3837d8905 100644 --- a/backend/app/repo/setting.go +++ b/backend/app/repo/setting.go @@ -3,20 +3,23 @@ package repo import ( "github.com/1Panel-dev/1Panel/app/model" "github.com/1Panel-dev/1Panel/global" + "gorm.io/gorm" ) type SettingRepo struct{} type ISettingRepo interface { - Get(opts ...DBOption) ([]model.Setting, error) + GetList(opts ...DBOption) ([]model.Setting, error) + Get(opts ...DBOption) (model.Setting, error) Update(key, value string) error + WithByKey(key string) DBOption } -func NewISettingService() ISettingRepo { +func NewISettingRepo() ISettingRepo { return &SettingRepo{} } -func (u *SettingRepo) Get(opts ...DBOption) ([]model.Setting, error) { +func (u *SettingRepo) GetList(opts ...DBOption) ([]model.Setting, error) { var settings []model.Setting db := global.DB.Model(&model.Setting{}) for _, opt := range opts { @@ -26,6 +29,22 @@ func (u *SettingRepo) Get(opts ...DBOption) ([]model.Setting, error) { return settings, err } +func (u *SettingRepo) Get(opts ...DBOption) (model.Setting, error) { + var settings model.Setting + db := global.DB.Model(&model.Setting{}) + for _, opt := range opts { + db = opt(db) + } + err := db.First(&settings).Error + return settings, err +} + +func (c *SettingRepo) WithByKey(key string) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("key = ?", key) + } +} + func (u *SettingRepo) Update(key, value string) error { return global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error } diff --git a/backend/app/repo/user.go b/backend/app/repo/user.go deleted file mode 100644 index 08aeab20e..000000000 --- a/backend/app/repo/user.go +++ /dev/null @@ -1,75 +0,0 @@ -package repo - -import ( - "github.com/1Panel-dev/1Panel/app/model" - "github.com/1Panel-dev/1Panel/global" -) - -type UserRepo struct{} - -type IUserRepo interface { - Get(opts ...DBOption) (model.User, error) - GetList(opts ...DBOption) ([]model.User, error) - Page(limit, offset int, opts ...DBOption) (int64, []model.User, error) - Create(user *model.User) error - Update(id uint, vars map[string]interface{}) error - Save(user model.User) error - Delete(opts ...DBOption) error -} - -func NewIUserService() IUserRepo { - return &UserRepo{} -} - -func (u *UserRepo) Get(opts ...DBOption) (model.User, error) { - var user model.User - db := global.DB - for _, opt := range opts { - db = opt(db) - } - err := db.First(&user).Error - return user, err -} - -func (u *UserRepo) GetList(opts ...DBOption) ([]model.User, error) { - var users []model.User - db := global.DB - for _, opt := range opts { - db = opt(db) - } - err := db.Find(&users).Error - return users, err -} - -func (u *UserRepo) Page(page, size int, opts ...DBOption) (int64, []model.User, error) { - var users []model.User - db := global.DB.Model(&model.User{}) - 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 -} - -func (u *UserRepo) Create(user *model.User) error { - return global.DB.Create(user).Error -} - -func (u *UserRepo) Update(id uint, vars map[string]interface{}) error { - return global.DB.Model(&model.User{}).Where("id = ?", id).Updates(vars).Error -} - -func (u *UserRepo) Save(user model.User) error { - return global.DB.Save(user).Error -} - -func (u *UserRepo) Delete(opts ...DBOption) error { - db := global.DB - for _, opt := range opts { - db = opt(db) - } - - return db.Delete(&model.User{}).Error -} diff --git a/backend/app/service/auth.go b/backend/app/service/auth.go new file mode 100644 index 000000000..db5702ca0 --- /dev/null +++ b/backend/app/service/auth.go @@ -0,0 +1,91 @@ +package service + +import ( + "strconv" + + "github.com/1Panel-dev/1Panel/app/dto" + "github.com/1Panel-dev/1Panel/constant" + "github.com/1Panel-dev/1Panel/global" + "github.com/1Panel-dev/1Panel/utils/encrypt" + "github.com/1Panel-dev/1Panel/utils/jwt" + "github.com/gin-gonic/gin" + "github.com/pkg/errors" + uuid "github.com/satori/go.uuid" +) + +type AuthService struct{} + +type IAuthService interface { + Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) + LogOut(c *gin.Context) error +} + +func NewIAuthService() IAuthService { + return &AuthService{} +} + +func (u *AuthService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) { + nameSetting, err := settingRepo.Get(settingRepo.WithByKey("UserName")) + if err != nil { + return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error()) + } + passwrodSetting, err := settingRepo.Get(settingRepo.WithByKey("Password")) + if err != nil { + return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error()) + } + pass, err := encrypt.StringDecrypt(passwrodSetting.Value) + if err != nil { + return nil, err + } + if info.Password != pass { + return nil, errors.New("login failed") + } + setting, err := settingRepo.Get(settingRepo.WithByKey("SessionTimeout")) + if err != nil { + return nil, err + } + lifeTime, err := strconv.Atoi(setting.Value) + if err != nil { + return nil, err + } + + if info.AuthMethod == constant.AuthMethodJWT { + j := jwt.NewJWT() + claims := j.CreateClaims(jwt.BaseClaims{ + Name: nameSetting.Value, + }, lifeTime) + token, err := j.CreateToken(claims) + if err != nil { + return nil, err + } + return &dto.UserLoginInfo{Name: nameSetting.Value, Token: token}, err + } + sID, _ := c.Cookie(constant.SessionName) + sessionUser, err := global.SESSION.Get(sID) + if err != nil { + sID = uuid.NewV4().String() + c.SetCookie(constant.SessionName, sID, lifeTime, "", "", false, false) + err := global.SESSION.Set(sID, sessionUser, lifeTime) + if err != nil { + return nil, err + } + return &dto.UserLoginInfo{Name: nameSetting.Value}, nil + } + if err := global.SESSION.Set(sID, sessionUser, lifeTime); err != nil { + return nil, err + } + + return &dto.UserLoginInfo{Name: nameSetting.Value}, nil +} + +func (u *AuthService) LogOut(c *gin.Context) error { + sID, _ := c.Cookie(constant.SessionName) + if sID != "" { + c.SetCookie(constant.SessionName, sID, -1, "", "", false, false) + err := global.SESSION.Delete(sID) + if err != nil { + return err + } + } + return nil +} diff --git a/backend/app/service/entry.go b/backend/app/service/entry.go index fbf3c36c3..10f907f81 100644 --- a/backend/app/service/entry.go +++ b/backend/app/service/entry.go @@ -3,7 +3,7 @@ package service import "github.com/1Panel-dev/1Panel/app/repo" type ServiceGroup struct { - UserService + AuthService HostService GroupService CommandService @@ -15,7 +15,6 @@ type ServiceGroup struct { var ServiceGroupApp = new(ServiceGroup) var ( - userRepo = repo.RepoGroupApp.UserRepo hostRepo = repo.RepoGroupApp.HostRepo groupRepo = repo.RepoGroupApp.GroupRepo commandRepo = repo.RepoGroupApp.CommandRepo diff --git a/backend/app/service/setting.go b/backend/app/service/setting.go index e4dacaf79..6eabfdd1d 100644 --- a/backend/app/service/setting.go +++ b/backend/app/service/setting.go @@ -6,13 +6,17 @@ import ( "github.com/1Panel-dev/1Panel/app/dto" "github.com/1Panel-dev/1Panel/constant" + "github.com/1Panel-dev/1Panel/global" + "github.com/1Panel-dev/1Panel/utils/encrypt" + "github.com/gin-gonic/gin" ) type SettingService struct{} type ISettingService interface { GetSettingInfo() (*dto.SettingInfo, error) - Update(key, value string) error + Update(c *gin.Context, key, value string) error + UpdatePassword(c *gin.Context, old, new string) error } func NewISettingService() ISettingService { @@ -20,7 +24,7 @@ func NewISettingService() ISettingService { } func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) { - setting, err := settingRepo.Get() + setting, err := settingRepo.GetList() if err != nil { return nil, constant.ErrRecordNotFound } @@ -40,6 +44,50 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) { return &info, err } -func (u *SettingService) Update(key, value string) error { +func (u *SettingService) Update(c *gin.Context, key, value string) error { + if err := settingRepo.Update(key, value); err != nil { + return err + } + switch key { + case "UserName": + sID, _ := c.Cookie(constant.SessionName) + if sID != "" { + c.SetCookie(constant.SessionName, sID, -1, "", "", false, false) + err := global.SESSION.Delete(sID) + if err != nil { + return err + } + } + } return settingRepo.Update(key, value) } + +func (u *SettingService) UpdatePassword(c *gin.Context, old, new string) error { + setting, err := settingRepo.Get(settingRepo.WithByKey("Password")) + if err != nil { + return err + } + passwordFromDB, err := encrypt.StringDecrypt(setting.Value) + if err != nil { + return err + } + if passwordFromDB == old { + newPassword, err := encrypt.StringEncrypt(new) + if err != nil { + return err + } + if err := settingRepo.Update("Password", newPassword); err != nil { + return err + } + sID, _ := c.Cookie(constant.SessionName) + if sID != "" { + c.SetCookie(constant.SessionName, sID, -1, "", "", false, false) + err := global.SESSION.Delete(sID) + if err != nil { + return err + } + } + return nil + } + return constant.ErrInitialPassword +} diff --git a/backend/app/service/user.go b/backend/app/service/user.go deleted file mode 100644 index 6f991388b..000000000 --- a/backend/app/service/user.go +++ /dev/null @@ -1,139 +0,0 @@ -package service - -import ( - "github.com/1Panel-dev/1Panel/app/dto" - "github.com/1Panel-dev/1Panel/app/model" - "github.com/1Panel-dev/1Panel/constant" - "github.com/1Panel-dev/1Panel/global" - "github.com/1Panel-dev/1Panel/utils/encrypt" - "github.com/1Panel-dev/1Panel/utils/jwt" - "github.com/gin-gonic/gin" - "github.com/jinzhu/copier" - "github.com/pkg/errors" - uuid "github.com/satori/go.uuid" -) - -type UserService struct{} - -type IUserService interface { - Get(id uint) (*dto.UserInfo, error) - Page(search dto.SearchWithPage) (int64, interface{}, error) - Register(userDto dto.UserCreate) error - Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) - LogOut(c *gin.Context) error - Delete(name string) error - Save(req model.User) error - Update(id uint, upMap map[string]interface{}) error - BatchDelete(ids []uint) error -} - -func NewIUserService() IUserService { - return &UserService{} -} - -func (u *UserService) Get(id uint) (*dto.UserInfo, error) { - user, err := userRepo.Get(commonRepo.WithByID(id)) - if err != nil { - return nil, constant.ErrRecordNotFound - } - var dtoUser dto.UserInfo - if err := copier.Copy(&dtoUser, &user); err != nil { - return nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) - } - return &dtoUser, err -} - -func (u *UserService) Page(search dto.SearchWithPage) (int64, interface{}, error) { - total, users, err := userRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info)) - var dtoUsers []dto.UserInfo - for _, user := range users { - var item dto.UserInfo - if err := copier.Copy(&item, &user); err != nil { - return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) - } - dtoUsers = append(dtoUsers, item) - } - return total, dtoUsers, err -} - -func (u *UserService) Register(userDto dto.UserCreate) error { - user, _ := userRepo.Get(commonRepo.WithByName(userDto.Name)) - if user.ID != 0 { - return constant.ErrRecordExist - } - if err := copier.Copy(&user, &userDto); err != nil { - return errors.WithMessage(constant.ErrStructTransform, err.Error()) - } - return userRepo.Create(&user) -} - -func (u *UserService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) { - user, err := userRepo.Get(commonRepo.WithByName(info.Name)) - if err != nil { - return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error()) - } - pass, err := encrypt.StringDecrypt(user.Password) - if err != nil { - return nil, err - } - if info.Password != pass { - return nil, errors.New("login failed") - } - if info.AuthMethod == constant.AuthMethodJWT { - j := jwt.NewJWT() - claims := j.CreateClaims(jwt.BaseClaims{ - ID: user.ID, - Name: user.Name, - }) - token, err := j.CreateToken(claims) - if err != nil { - return nil, err - } - return &dto.UserLoginInfo{Name: user.Name, Token: token}, err - } - lifeTime := global.CONF.Session.ExpiresTime - sID, _ := c.Cookie(global.CONF.Session.SessionName) - sessionUser, err := global.SESSION.Get(sID) - if err != nil { - sID = uuid.NewV4().String() - c.SetCookie(global.CONF.Session.SessionName, sID, lifeTime, "", "", false, false) - err := global.SESSION.Set(sID, sessionUser, lifeTime) - if err != nil { - return nil, err - } - return &dto.UserLoginInfo{Name: user.Name}, nil - } - if err := global.SESSION.Set(sID, sessionUser, lifeTime); err != nil { - return nil, err - } - - return &dto.UserLoginInfo{Name: user.Name}, nil -} - -func (u *UserService) LogOut(c *gin.Context) error { - sID, _ := c.Cookie(global.CONF.Session.SessionName) - if sID != "" { - c.SetCookie(global.CONF.Session.SessionName, sID, -1, "", "", false, false) - err := global.SESSION.Delete(sID) - if err != nil { - return err - } - } - return nil -} - -func (u *UserService) Delete(name string) error { - return userRepo.Delete(commonRepo.WithByName(name)) -} - -func (u *UserService) BatchDelete(ids []uint) error { - return userRepo.Delete(commonRepo.WithIdsIn(ids)) -} - -func (u *UserService) Save(req model.User) error { - return userRepo.Save(req) -} - -func (u *UserService) Update(id uint, upMap map[string]interface{}) error { - return userRepo.Update(id, upMap) -} diff --git a/backend/configs/captcha.go b/backend/configs/captcha.go deleted file mode 100644 index 25510e8a6..000000000 --- a/backend/configs/captcha.go +++ /dev/null @@ -1,10 +0,0 @@ -package configs - -type Captcha struct { - Enable bool `mapstructure:"enable" json:"enable" yaml:"enable"` - Source string `mapstructure:"source" json:"source" yaml:"source"` - Length int `mapstructure:"length" json:"length" yaml:"length"` - NoiseCount int `mapstructure:"noise-count" json:"noise-count" yaml:"noise-count"` - ImgWidth int `mapstructure:"img-width" json:"img-width" yaml:"img-width"` - ImgHeight int `mapstructure:"img-height" json:"img-height" yaml:"img-height"` -} diff --git a/backend/configs/config.go b/backend/configs/config.go index c977c48ea..85f8abb7d 100644 --- a/backend/configs/config.go +++ b/backend/configs/config.go @@ -5,10 +5,7 @@ type ServerConfig struct { Mysql Mysql `mapstructure:"mysql"` System System `mapstructure:"system"` LogConfig LogConfig `mapstructure:"log"` - JWT JWT `mapstructure:"jwt"` - Session Session `mapstructure:"session"` CORS CORS `mapstructure:"cors"` - Captcha Captcha `mapstructure:"captcha"` Encrypt Encrypt `mapstructure:"encrypt"` Csrf Csrf `mapstructure:"csrf"` Cache Cache `mapstructure:"cache"` diff --git a/backend/configs/jwt.go b/backend/configs/jwt.go deleted file mode 100644 index 6f0b0ab88..000000000 --- a/backend/configs/jwt.go +++ /dev/null @@ -1,9 +0,0 @@ -package configs - -type JWT struct { - HeaderName string `mapstructure:"header_name"` - SigningKey string `mapstructure:"signing_key"` - ExpiresTime int64 `mapstructure:"expires_time"` - BufferTime int64 `mapstructure:"buffer_time"` - Issuer string `mapstructure:"issuer"` -} diff --git a/backend/configs/session.go b/backend/configs/session.go deleted file mode 100644 index c9aa7db8b..000000000 --- a/backend/configs/session.go +++ /dev/null @@ -1,6 +0,0 @@ -package configs - -type Session struct { - SessionName string `mapstructure:"session_name"` - ExpiresTime int `mapstructure:"expires_time"` -} diff --git a/backend/constant/errs.go b/backend/constant/errs.go index 73e23ad6e..1a8674340 100644 --- a/backend/constant/errs.go +++ b/backend/constant/errs.go @@ -20,6 +20,7 @@ var ( ErrRecordExist = errors.New("ErrRecordExist") ErrRecordNotFound = errors.New("ErrRecordNotFound") ErrStructTransform = errors.New("ErrStructTransform") + ErrInitialPassword = errors.New("ErrInitialPassword") ErrTokenParse = errors.New("ErrTokenParse") diff --git a/backend/constant/session.go b/backend/constant/session.go index 8fb81212b..6c7017c31 100644 --- a/backend/constant/session.go +++ b/backend/constant/session.go @@ -2,5 +2,11 @@ package constant const ( AuthMethodSession = "session" - AuthMethodJWT = "jwt" + SessionName = "psession" + + AuthMethodJWT = "jwt" + JWTHeaderName = "Authorization" + JWTSigningKey = "1panelKey" + JWTBufferTime = 86400 + JWTIssuer = "1Panel" ) diff --git a/backend/i18n/lang/en.yaml b/backend/i18n/lang/en.yaml index 5e955c163..a5c51128a 100644 --- a/backend/i18n/lang/en.yaml +++ b/backend/i18n/lang/en.yaml @@ -3,6 +3,7 @@ ErrToken: "Token information is incorrect.: {{ .detail }}" ErrTokenParse: "Token generation error: {{ .detail }}" ErrTokenTimeOut: "Login information is out of date: {{ .detail }}" ErrCaptchaCode: "The verification code information is incorrect" +ErrInitialPassword: "Initial password error: {{ .detail }}" ErrInternalServer: "Service internal error: {{ .detail }}" ErrRecordExist: "Record already exists: {{ .detail }}" ErrRecordNotFound: "Records not found: {{ .detail }}" diff --git a/backend/i18n/lang/zh.yaml b/backend/i18n/lang/zh.yaml index 14fad1bbe..cb2d0bf05 100644 --- a/backend/i18n/lang/zh.yaml +++ b/backend/i18n/lang/zh.yaml @@ -3,6 +3,7 @@ ErrToken: "Token 信息错误: {{ .detail }}" ErrTokenParse: "Token 生成错误: {{ .detail }}" ErrTokenTimeOut: "登陆信息已过期: {{ .detail }}" ErrCaptchaCode: "错误的验证码信息" +ErrInitialPassword: "初始密码错误: {{ .detail }}" ErrInternalServer: "服务内部错误: {{ .detail }}" ErrRecordExist: "记录已存在: {{ .detail }}" ErrRecordNotFound: "记录未能找到: {{ .detail }}" diff --git a/backend/init/migration/migrate.go b/backend/init/migration/migrate.go index 4030df53c..c6a60eaac 100644 --- a/backend/init/migration/migrate.go +++ b/backend/init/migration/migrate.go @@ -9,8 +9,6 @@ import ( func Init() { m := gormigrate.New(global.DB, gormigrate.DefaultOptions, []*gormigrate.Migration{ - migrations.InitTable, - migrations.AddData, migrations.AddTableOperationLog, migrations.AddTableHost, migrations.AddTableMonitor, diff --git a/backend/init/migration/migrations/init.go b/backend/init/migration/migrations/init.go index 9b6f6584e..56008f3f6 100644 --- a/backend/init/migration/migrations/init.go +++ b/backend/init/migration/migrations/init.go @@ -9,24 +9,6 @@ import ( "gorm.io/gorm" ) -var InitTable = &gormigrate.Migration{ - ID: "20220803-init-table", - Migrate: func(tx *gorm.DB) error { - return tx.AutoMigrate(&model.User{}) - }, -} - -var user = model.User{ - Name: "admin", Email: "admin@fit2cloud.com", Password: "5WYEZ4XcitdomVvAyimt9WwJwBJJSbTTHncZoqyOraQ=", -} - -var AddData = &gormigrate.Migration{ - ID: "20200803-add-data", - Migrate: func(tx *gorm.DB) error { - return tx.Create(&user).Error - }, -} - var AddTableOperationLog = &gormigrate.Migration{ ID: "20200809-add-table-operation-log", Migrate: func(tx *gorm.DB) error { diff --git a/backend/init/router/router.go b/backend/init/router/router.go index 5b5f22d58..008e43d16 100644 --- a/backend/init/router/router.go +++ b/backend/init/router/router.go @@ -38,7 +38,6 @@ func Routers() *gin.Engine { PrivateGroup := Router.Group("/api/v1") { systemRouter.InitBaseRouter(PrivateGroup) - systemRouter.InitUserRouter(PrivateGroup) systemRouter.InitHostRouter(PrivateGroup) systemRouter.InitGroupRouter(PrivateGroup) systemRouter.InitCommandRouter(PrivateGroup) diff --git a/backend/middleware/jwt.go b/backend/middleware/jwt.go index c3f564d7d..651306dc5 100644 --- a/backend/middleware/jwt.go +++ b/backend/middleware/jwt.go @@ -1,9 +1,11 @@ package middleware import ( + "strconv" "time" "github.com/1Panel-dev/1Panel/app/api/v1/helper" + "github.com/1Panel-dev/1Panel/app/repo" "github.com/1Panel-dev/1Panel/constant" "github.com/1Panel-dev/1Panel/global" jwtUtils "github.com/1Panel-dev/1Panel/utils/jwt" @@ -15,7 +17,7 @@ import ( func JwtAuth() gin.HandlerFunc { return func(c *gin.Context) { c.Set("authMethod", "") - token := c.Request.Header.Get(global.CONF.JWT.HeaderName) + token := c.Request.Header.Get(constant.JWTHeaderName) if token == "" { c.Next() return @@ -27,7 +29,13 @@ func JwtAuth() gin.HandlerFunc { return } if claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime { - claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(global.CONF.JWT.ExpiresTime))) + settingRepo := repo.NewISettingRepo() + setting, err := settingRepo.Get(settingRepo.WithByKey("SessionTimeout")) + if err != nil { + global.LOG.Errorf("create operation record failed, err: %v", err) + } + lifeTime, _ := strconv.Atoi(setting.Value) + claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(lifeTime))) } c.Set("claims", claims) c.Set("authMethod", constant.AuthMethodJWT) diff --git a/backend/middleware/session.go b/backend/middleware/session.go index ba879cb9f..5ee4fb955 100644 --- a/backend/middleware/session.go +++ b/backend/middleware/session.go @@ -12,7 +12,7 @@ func SessionAuth() gin.HandlerFunc { if method, exist := c.Get("authMethod"); exist && method == constant.AuthMethodJWT { c.Next() } - sId, err := c.Cookie(global.CONF.Session.SessionName) + sId, err := c.Cookie(constant.SessionName) if err != nil { helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrTypeNotLogin, nil) return diff --git a/backend/router/entry.go b/backend/router/entry.go index 0751e6866..76109230c 100644 --- a/backend/router/entry.go +++ b/backend/router/entry.go @@ -2,13 +2,13 @@ package router type RouterGroup struct { BaseRouter - UserRouter HostRouter GroupRouter CommandRouter MonitorRouter OperationLogRouter FileRouter + TerminalRouter SettingRouter } diff --git a/backend/router/ro_setting.go b/backend/router/ro_setting.go index 1fa013d5c..4dd10ffdd 100644 --- a/backend/router/ro_setting.go +++ b/backend/router/ro_setting.go @@ -14,5 +14,6 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) { { settingRouter.POST("/search", baseApi.GetSettingInfo) settingRouter.PUT("", baseApi.UpdateSetting) + settingRouter.PUT("/password", baseApi.UpdatePassword) } } diff --git a/backend/router/ro_terminal.go b/backend/router/ro_terminal.go index 37a87f712..7486d1022 100644 --- a/backend/router/ro_terminal.go +++ b/backend/router/ro_terminal.go @@ -9,7 +9,7 @@ import ( type TerminalRouter struct{} -func (s *UserRouter) InitTerminalRouter(Router *gin.RouterGroup) { +func (s *TerminalRouter) InitTerminalRouter(Router *gin.RouterGroup) { terminalRouter := Router.Group("terminals").Use(middleware.JwtAuth()).Use(middleware.SessionAuth()) baseApi := v1.ApiGroupApp.BaseApi { diff --git a/backend/router/ro_user.go b/backend/router/ro_user.go deleted file mode 100644 index 3fa26adc8..000000000 --- a/backend/router/ro_user.go +++ /dev/null @@ -1,24 +0,0 @@ -package router - -import ( - v1 "github.com/1Panel-dev/1Panel/app/api/v1" - "github.com/1Panel-dev/1Panel/middleware" - - "github.com/gin-gonic/gin" -) - -type UserRouter struct{} - -func (s *UserRouter) InitUserRouter(Router *gin.RouterGroup) { - userRouter := Router.Group("users") - userRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()) - withRecordRouter := userRouter.Use(middleware.OperationRecord()) - baseApi := v1.ApiGroupApp.BaseApi - { - withRecordRouter.POST("", baseApi.Register) - withRecordRouter.POST("/del", baseApi.DeleteUser) - userRouter.POST("/search", baseApi.PageUsers) - userRouter.GET(":id", baseApi.GetUserInfo) - userRouter.PUT(":id", baseApi.UpdateUser) - } -} diff --git a/backend/utils/captcha/captcha.go b/backend/utils/captcha/captcha.go index 7eb736d89..97729b01b 100644 --- a/backend/utils/captcha/captcha.go +++ b/backend/utils/captcha/captcha.go @@ -5,16 +5,12 @@ import ( "github.com/1Panel-dev/1Panel/app/dto" "github.com/1Panel-dev/1Panel/constant" - "github.com/1Panel-dev/1Panel/global" "github.com/mojocn/base64Captcha" ) var store = base64Captcha.DefaultMemStore func VerifyCode(codeID string, code string) error { - if !global.CONF.Captcha.Enable { - return nil - } if codeID == "" { return constant.ErrCaptchaCode } @@ -30,11 +26,11 @@ func VerifyCode(codeID string, code string) error { func CreateCaptcha() (*dto.CaptchaResponse, error) { var driverString base64Captcha.DriverString - driverString.Source = global.CONF.Captcha.Source - driverString.Width = global.CONF.Captcha.ImgWidth - driverString.Height = global.CONF.Captcha.ImgHeight - driverString.NoiseCount = global.CONF.Captcha.NoiseCount - driverString.Length = global.CONF.Captcha.Length + driverString.Source = "1234567890QWERTYUPLKJHGFDSAZXCVBNMqwertyuplkjhgfdsazxcvbnm" + driverString.Width = 120 + driverString.Height = 50 + driverString.NoiseCount = 0 + driverString.Length = 4 driverString.Fonts = []string{"wqy-microhei.ttc"} driver := driverString.ConvertFonts() c := base64Captcha.NewCaptcha(driver, store) diff --git a/backend/utils/jwt/jwt.go b/backend/utils/jwt/jwt.go index f30bd3b99..afbaadd58 100644 --- a/backend/utils/jwt/jwt.go +++ b/backend/utils/jwt/jwt.go @@ -4,7 +4,6 @@ import ( "time" "github.com/1Panel-dev/1Panel/constant" - "github.com/1Panel-dev/1Panel/global" "github.com/golang-jwt/jwt/v4" ) @@ -32,17 +31,17 @@ type BaseClaims struct { func NewJWT() *JWT { return &JWT{ - []byte(global.CONF.JWT.SigningKey), + []byte(constant.JWTSigningKey), } } -func (j *JWT) CreateClaims(baseClaims BaseClaims) CustomClaims { +func (j *JWT) CreateClaims(baseClaims BaseClaims, ttl int) CustomClaims { claims := CustomClaims{ BaseClaims: baseClaims, - BufferTime: global.CONF.JWT.BufferTime, + BufferTime: constant.JWTBufferTime, RegisteredClaims: jwt.RegisteredClaims{ - ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(global.CONF.JWT.ExpiresTime))), - Issuer: global.CONF.JWT.Issuer, + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(ttl))), + Issuer: constant.JWTIssuer, }, } return claims diff --git a/frontend/src/api/modules/setting.ts b/frontend/src/api/modules/setting.ts index 94eb00f02..f2ca59906 100644 --- a/frontend/src/api/modules/setting.ts +++ b/frontend/src/api/modules/setting.ts @@ -8,3 +8,7 @@ export const getSettingInfo = () => { export const updateSetting = (param: Setting.SettingUpdate) => { return http.put(`/settings`, param); }; + +export const updatePassword = (param: Setting.PasswordUpdate) => { + return http.put(`/settings/password`, param); +}; diff --git a/frontend/src/views/setting/tabs/panel.vue b/frontend/src/views/setting/tabs/panel.vue index badbbd540..ad6449a4b 100644 --- a/frontend/src/views/setting/tabs/panel.vue +++ b/frontend/src/views/setting/tabs/panel.vue @@ -144,12 +144,13 @@