From d5f400670c81ecb60d4dfb7780906ea3ac808731 Mon Sep 17 00:00:00 2001 From: ssongliu Date: Fri, 14 Apr 2023 18:54:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E5=85=A5=E5=8F=A3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/auth.go | 39 ++-------- backend/app/dto/setting.go | 1 + backend/app/service/auth.go | 39 +++------- backend/app/service/setting.go | 10 +++ backend/init/migration/migrate.go | 1 + backend/init/migration/migrations/init.go | 13 ++++ backend/init/router/router.go | 3 - backend/middleware/safety.go | 18 ----- backend/router/ro_base.go | 3 +- cmd/server/cmd/user-info.go | 2 + frontend/src/api/helper/check-status.ts | 4 +- frontend/src/api/index.ts | 11 +-- frontend/src/api/interface/setting.ts | 1 + frontend/src/api/modules/auth.ts | 6 +- .../src/components/app-layout/menu/index.vue | 2 +- frontend/src/lang/modules/en.ts | 11 +-- frontend/src/lang/modules/zh.ts | 11 +-- frontend/src/routers/index.ts | 3 +- frontend/src/routers/router.ts | 2 +- frontend/src/store/index.ts | 1 + frontend/src/store/interface/index.ts | 1 + frontend/src/views/login/index.vue | 57 ++++++++++----- frontend/src/views/setting/panel/index.vue | 4 +- .../views/setting/panel/password/index.vue | 2 +- frontend/src/views/setting/safe/index.vue | 73 +++++++++++++++++++ 25 files changed, 181 insertions(+), 137 deletions(-) delete mode 100644 backend/middleware/safety.go diff --git a/backend/app/api/v1/auth.go b/backend/app/api/v1/auth.go index a06479d8e..c576b6fb6 100644 --- a/backend/app/api/v1/auth.go +++ b/backend/app/api/v1/auth.go @@ -1,8 +1,6 @@ package v1 import ( - "errors" - "github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" "github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/model" @@ -102,44 +100,17 @@ func (b *BaseApi) Captcha(c *gin.Context) { // @Summary Load safety status // @Description 获取系统安全登录状态 // @Success 200 -// @Failure 402 -// @Router /auth/status [get] -func (b *BaseApi) GetSafetyStatus(c *gin.Context) { - if err := authService.SafetyStatus(c); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, err) - return - } - helper.SuccessWithData(c, nil) -} - -func (b *BaseApi) SafeEntrance(c *gin.Context) { - code, exist := c.Params.Get("code") - if !exist { - helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code")) - return - } - ok, err := authService.VerifyCode(code) - if err != nil { - helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code")) - return - } - if !ok { - helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code")) - return - } - if err := authService.SafeEntrance(c, code); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code")) - return - } - - helper.SuccessWithData(c, nil) +// @Router /auth/issafety [get] +func (b *BaseApi) CheckIsSafety(c *gin.Context) { + code := c.DefaultQuery("code", "") + helper.SuccessWithData(c, authService.CheckIsSafety(code)) } // @Tags Auth // @Summary Check is First login // @Description 判断是否为首次登录 // @Success 200 -// @Router /auth/status [get] +// @Router /auth/isfirst [get] func (b *BaseApi) CheckIsFirstLogin(c *gin.Context) { helper.SuccessWithData(c, authService.CheckIsFirst()) } diff --git a/backend/app/dto/setting.go b/backend/app/dto/setting.go index ee399ccbe..e4ad1fa4d 100644 --- a/backend/app/dto/setting.go +++ b/backend/app/dto/setting.go @@ -16,6 +16,7 @@ type SettingInfo struct { Language string `json:"language"` ServerPort string `json:"serverPort"` + SecurityEntranceStatus string `json:"securityEntranceStatus"` SecurityEntrance string `json:"securityEntrance"` ExpirationDays string `json:"expirationDays"` ExpirationTime string `json:"expirationTime"` diff --git a/backend/app/service/auth.go b/backend/app/service/auth.go index b45dcad37..a653a041f 100644 --- a/backend/app/service/auth.go +++ b/backend/app/service/auth.go @@ -19,11 +19,10 @@ import ( type AuthService struct{} type IAuthService interface { - SafetyStatus(c *gin.Context) error + CheckIsSafety(code string) bool CheckIsFirst() bool InitUser(c *gin.Context, req dto.InitUser) error VerifyCode(code string) (bool, error) - SafeEntrance(c *gin.Context, code string) error Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) LogOut(c *gin.Context) error MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLoginInfo, error) @@ -33,22 +32,6 @@ func NewIAuthService() IAuthService { return &AuthService{} } -func (u *AuthService) SafeEntrance(c *gin.Context, code string) error { - codeWithMD5 := encrypt.Md5(code) - cookieValue, _ := encrypt.StringEncrypt(codeWithMD5) - c.SetCookie(codeWithMD5, cookieValue, 604800, "", "", false, false) - - expiredSetting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays")) - if err != nil { - return err - } - timeout, _ := strconv.Atoi(expiredSetting.Value) - if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil { - return err - } - return nil -} - func (u *AuthService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) { nameSetting, err := settingRepo.Get(settingRepo.WithByKey("UserName")) if err != nil { @@ -164,23 +147,19 @@ func (u *AuthService) VerifyCode(code string) (bool, error) { return setting.Value == code, nil } -func (u *AuthService) SafetyStatus(c *gin.Context) error { - setting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance")) +func (u *AuthService) CheckIsSafety(code string) bool { + status, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntranceStatus")) if err != nil { - return err + return false } - codeWithEcrypt, err := c.Cookie(encrypt.Md5(setting.Value)) - if err != nil { - return err + if status.Value == "disable" { + return true } - code, err := encrypt.StringDecrypt(codeWithEcrypt) + setting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance")) if err != nil { - return err - } - if code != encrypt.Md5(setting.Value) { - return errors.New("code not match") + return false } - return nil + return setting.Value == code } func (u *AuthService) CheckIsFirst() bool { diff --git a/backend/app/service/setting.go b/backend/app/service/setting.go index 454023805..15ec6cfba 100644 --- a/backend/app/service/setting.go +++ b/backend/app/service/setting.go @@ -57,6 +57,16 @@ func (u *SettingService) Update(key, value string) error { return err } } + if key == "SecurityEntrance" { + if err := settingRepo.Update("SecurityEntranceStatus", "enable"); err != nil { + return err + } + } + if key == "SecurityEntranceStatus" { + if err := settingRepo.Update("SecurityEntrance", ""); err != nil { + return err + } + } if err := settingRepo.Update(key, value); err != nil { return err } diff --git a/backend/init/migration/migrate.go b/backend/init/migration/migrate.go index 597519e0f..b1d79d7b4 100644 --- a/backend/init/migration/migrate.go +++ b/backend/init/migration/migrate.go @@ -25,6 +25,7 @@ func Init() { migrations.UpdateTableApp, migrations.UpdateTableHost, migrations.UpdateTableWebsite, + migrations.AddEntranceStatus, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/backend/init/migration/migrations/init.go b/backend/init/migration/migrations/init.go index bf934a602..f6dc843b5 100644 --- a/backend/init/migration/migrations/init.go +++ b/backend/init/migration/migrations/init.go @@ -287,3 +287,16 @@ var UpdateTableWebsite = &gormigrate.Migration{ return nil }, } + +var AddEntranceStatus = &gormigrate.Migration{ + ID: "20230414-add-entrance-status", + Migrate: func(tx *gorm.DB) error { + if err := tx.Create(&model.Setting{Key: "SecurityEntranceStatus", Value: "disable"}).Error; err != nil { + return err + } + if err := tx.Model(&model.Setting{}).Where("key = ?", "SecurityEntrance").Updates(map[string]interface{}{"value": ""}).Error; err != nil { + return err + } + return tx.AutoMigrate(&model.Website{}) + }, +} diff --git a/backend/init/router/router.go b/backend/init/router/router.go index 4e106e5b3..c2a2ec62b 100644 --- a/backend/init/router/router.go +++ b/backend/init/router/router.go @@ -6,7 +6,6 @@ import ( "github.com/gin-contrib/gzip" - v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1" "github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/i18n" "github.com/1Panel-dev/1Panel/backend/middleware" @@ -53,8 +52,6 @@ func Routers() *gin.Engine { setWebStatic(Router) Router.Use(i18n.GinI18nLocalize()) - Router.GET("/api/v1/info", v1.ApiGroupApp.BaseApi.GetSafetyStatus) - Router.GET("/api/v1/:code", v1.ApiGroupApp.BaseApi.SafeEntrance) Router.SetFuncMap(template.FuncMap{ "Localize": ginI18n.GetMessage, diff --git a/backend/middleware/safety.go b/backend/middleware/safety.go deleted file mode 100644 index aadca6006..000000000 --- a/backend/middleware/safety.go +++ /dev/null @@ -1,18 +0,0 @@ -package middleware - -import ( - "github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" - "github.com/1Panel-dev/1Panel/backend/app/service" - "github.com/1Panel-dev/1Panel/backend/constant" - "github.com/gin-gonic/gin" -) - -func SafetyAuth() gin.HandlerFunc { - return func(c *gin.Context) { - if err := service.NewIAuthService().SafetyStatus(c); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, nil) - return - } - c.Next() - } -} diff --git a/backend/router/ro_base.go b/backend/router/ro_base.go index c57666ad6..d854f0fde 100644 --- a/backend/router/ro_base.go +++ b/backend/router/ro_base.go @@ -14,7 +14,8 @@ func (s *BaseRouter) InitBaseRouter(Router *gin.RouterGroup) { baseRouter.GET("/captcha", baseApi.Captcha) baseRouter.POST("/mfalogin", baseApi.MFALogin) baseRouter.POST("/login", baseApi.Login) - baseRouter.GET("/status", baseApi.CheckIsFirstLogin) + baseRouter.GET("/isfirst", baseApi.CheckIsFirstLogin) + baseRouter.GET("/issafety", baseApi.CheckIsSafety) baseRouter.POST("/init", baseApi.InitUserInfo) baseRouter.POST("/logout", baseApi.LogOut) baseRouter.GET("/demo", baseApi.CheckIsDemo) diff --git a/cmd/server/cmd/user-info.go b/cmd/server/cmd/user-info.go index 1e04a39ec..fe64dacc2 100644 --- a/cmd/server/cmd/user-info.go +++ b/cmd/server/cmd/user-info.go @@ -39,6 +39,7 @@ var userinfoCmd = &cobra.Command{ user := getSettingByKey(db, "UserName") password := getSettingByKey(db, "Password") port := getSettingByKey(db, "ServerPort") + entrance := getSettingByKey(db, "SecurityEntrance") enptrySetting := getSettingByKey(db, "EncryptKey") p := "" @@ -49,6 +50,7 @@ var userinfoCmd = &cobra.Command{ p = password } + fmt.Printf("entrance: %s\n", entrance) fmt.Printf("username: %s\n", user) fmt.Printf("password: %s\n", p) fmt.Printf("port: %s\n", port) diff --git a/frontend/src/api/helper/check-status.ts b/frontend/src/api/helper/check-status.ts index 514ba4126..213a7002e 100644 --- a/frontend/src/api/helper/check-status.ts +++ b/frontend/src/api/helper/check-status.ts @@ -1,6 +1,8 @@ import i18n from '@/lang'; import router from '@/routers'; import { MsgError } from '@/utils/message'; +import { GlobalStore } from '@/store'; +const globalStore = GlobalStore(); export const checkStatus = (status: number, msg: string): void => { switch (status) { @@ -11,7 +13,7 @@ export const checkStatus = (status: number, msg: string): void => { MsgError(msg ? msg : i18n.global.t('commons.res.notFound')); break; case 403: - router.replace({ path: '/login' }); + router.replace({ path: '/login', params: { code: globalStore.entrance } }); MsgError(msg ? msg : i18n.global.t('commons.res.forbidden')); break; case 500: diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index 493b83813..f2cececf5 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -43,17 +43,12 @@ class RequestHttp { globalStore.setCsrfToken(response.headers['x-csrf-token']); } if (data.code == ResultEnum.OVERDUE || data.code == ResultEnum.FORBIDDEN) { - router.replace({ - path: '/login', + router.push({ + name: 'login', + params: { code: globalStore.entrance }, }); return Promise.reject(data); } - if (data.code == ResultEnum.UNSAFETY) { - router.replace({ - path: '/login', - }); - return data; - } if (data.code == ResultEnum.EXPIRED) { router.push({ name: 'Expired' }); return data; diff --git a/frontend/src/api/interface/setting.ts b/frontend/src/api/interface/setting.ts index b411c34c3..51e7f2f14 100644 --- a/frontend/src/api/interface/setting.ts +++ b/frontend/src/api/interface/setting.ts @@ -15,6 +15,7 @@ export namespace Setting { language: string; serverPort: number; + securityEntranceStatus: string; securityEntrance: string; expirationDays: number; expirationTime: string; diff --git a/frontend/src/api/modules/auth.ts b/frontend/src/api/modules/auth.ts index f3808da86..72196fe5c 100644 --- a/frontend/src/api/modules/auth.ts +++ b/frontend/src/api/modules/auth.ts @@ -26,7 +26,11 @@ export const loginStatus = () => { }; export const checkIsFirst = () => { - return http.get('/auth/status'); + return http.get('/auth/isfirst'); +}; + +export const checkIsSafety = (code: string) => { + return http.get(`/auth/issafety?code=${code}`); }; export const initUser = (params: Login.InitUser) => { diff --git a/frontend/src/components/app-layout/menu/index.vue b/frontend/src/components/app-layout/menu/index.vue index 452ab29ff..cf5533813 100644 --- a/frontend/src/components/app-layout/menu/index.vue +++ b/frontend/src/components/app-layout/menu/index.vue @@ -80,7 +80,7 @@ const logout = () => { }) .then(() => { systemLogOut(); - router.push({ name: 'login' }); + router.push({ name: 'login', params: { code: globalStore.entrance } }); globalStore.setLogStatus(false); MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); }) diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index adfda7634..3dd1c7d34 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -114,15 +114,8 @@ const message = { errorMfaInfo: 'Incorrect authentication information, please try again!', captchaHelper: 'Captcha', errorCaptcha: 'Captcha code error!', - safeEntrance: 'Please use the correct entry to log in to the panel', - reason: 'Cause of error:', - reasonHelper: - 'At present, the newly installed machine has enabled the security entrance login. The newly installed machine will have a random 8-character security entrance name, which can also be modified in the panel Settings. If you do not record or do not remember, you can use the following methods to solve the problem', - solution: 'The solution:', - solutionHelper: - 'Run the following command on the SSH terminal to solve the problem: 1. View the /etc/init.d/bt default command on the panel', - warnning: - 'Note: [Closing the security entrance] will make your panel login address directly exposed to the Internet, very dangerous, please exercise caution', + safeEntrance: + 'The command "1pctl user-info" can be used in SSH terminal to view the panel entrance as the secure login has been enabled in the current environment.', codeInput: 'Please enter the 6-digit verification code of the MFA validator', mfaTitle: 'MFA Certification', mfaCode: 'MFA verification code', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 3f207528c..c63b5165c 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -118,13 +118,7 @@ const message = { errorMfaInfo: '错误的验证信息,请重试!', captchaHelper: '验证码', errorCaptcha: '验证码错误!', - safeEntrance: '请使用正确的入口登录面板', - reason: '错误原因:', - reasonHelper: - '当前新安装的已经开启了安全入口登录,新装机器都会随机一个8位字符的安全入口名称,亦可以在面板设置处修改,如您没记录或不记得了,可以使用以下方式解决', - solution: '解决方法:', - solutionHelper: '在 SSH 终端输入以下一种命令来解决 1.查看面板入口:/etc/init.d/bt default', - warnning: '注意:【关闭安全入口】将使您的面板登录地址被直接暴露在互联网上,非常危险,请谨慎操作', + safeEntrance: '当前环境已经开启了安全入口登录,在 SSH 终端输入以下命令来查看面板入口: 1pctl user-info', codeInput: '请输入 MFA 验证器的 6 位验证码', mfaTitle: 'MFA 认证', mfaCode: 'MFA 验证码', @@ -806,6 +800,9 @@ const message = { portHelper: '建议端口范围8888 - 65535,注意:有安全组的服务器请提前在安全组放行新端口', portChange: '端口修改', portChangeHelper: '服务端口修改需要重启服务,是否继续?', + entrance: '安全入口', + entranceHelper: '开启安全入口后只能通过指定安全入口登录面板', + entranceError: '请输入 8 位安全登录入口,仅支持输入数字或字母', theme: '主题颜色', componentSize: '组件大小', dark: '暗色', diff --git a/frontend/src/routers/index.ts b/frontend/src/routers/index.ts index 24ea87862..4b15fa54c 100644 --- a/frontend/src/routers/index.ts +++ b/frontend/src/routers/index.ts @@ -17,7 +17,8 @@ router.beforeEach((to, from, next) => { const globalStore = GlobalStore(); if (!globalStore.isLogin) { next({ - path: '/login', + name: 'login', + params: { code: globalStore.entrance }, }); NProgress.done(); return; diff --git a/frontend/src/routers/router.ts b/frontend/src/routers/router.ts index a48425caf..d245b6a65 100644 --- a/frontend/src/routers/router.ts +++ b/frontend/src/routers/router.ts @@ -58,7 +58,7 @@ menuList.unshift(homeRouter); export const routes: RouteRecordRaw[] = [ homeRouter, { - path: '/login', + path: '/login/:code?', name: 'login', props: true, component: () => import('@/views/login/index.vue'), diff --git a/frontend/src/store/index.ts b/frontend/src/store/index.ts index 04c54d6a5..1ebf73f77 100644 --- a/frontend/src/store/index.ts +++ b/frontend/src/store/index.ts @@ -12,6 +12,7 @@ export const GlobalStore = defineStore({ loadingText: '', isLogin: false, csrfToken: '', + entrance: '', language: '', themeConfig: { panelName: '', diff --git a/frontend/src/store/interface/index.ts b/frontend/src/store/interface/index.ts index 44bf0bba6..202471f27 100644 --- a/frontend/src/store/interface/index.ts +++ b/frontend/src/store/interface/index.ts @@ -11,6 +11,7 @@ export interface GlobalState { isLoading: boolean; loadingText: string; isLogin: boolean; + entrance: string; csrfToken: string; language: string; // zh | en // assemblySize: string; // small | default | large diff --git a/frontend/src/views/login/index.vue b/frontend/src/views/login/index.vue index f466737c5..4d80accda 100644 --- a/frontend/src/views/login/index.vue +++ b/frontend/src/views/login/index.vue @@ -1,6 +1,6 @@