diff --git a/core/app/auth/api_auth.go b/core/app/auth/api_auth.go index 9ff7aa1bf585..a218a6d9c623 100644 --- a/core/app/auth/api_auth.go +++ b/core/app/auth/api_auth.go @@ -20,7 +20,7 @@ type APIAuthConfig struct { ApiInterfaceStatus string ApiKey string IpWhiteList string - ApiKeyValidityTime string + ApiKeyValidityTime int } type APIAuthConfigLoader func(c *gin.Context) (APIAuthConfig, error) @@ -83,16 +83,19 @@ func LoadAPIAuthConfig(_ *gin.Context) (APIAuthConfig, error) { if config.IpWhiteList, err = settingRepo.GetValueByKey("IpWhiteList"); err != nil { return config, err } - if config.ApiKeyValidityTime, err = settingRepo.GetValueByKey("ApiKeyValidityTime"); err != nil { + apiValidity, err := settingRepo.GetValueByKey("ApiKeyValidityTime") + if err != nil { + return config, err + } + if config.ApiKeyValidityTime, err = strconv.Atoi(apiValidity); err != nil { return config, err } return config, nil } -func isValid1PanelTimestamp(panelTimestamp string, apiKeyValidityTime string) bool { - apiTime, err := strconv.Atoi(apiKeyValidityTime) - if err != nil || apiTime < 0 { - global.LOG.Errorf("apiTime %d, err: %v", apiTime, err) +func isValid1PanelTimestamp(panelTimestamp string, apiKeyValidityTime int) bool { + apiTime := apiKeyValidityTime + if apiTime < 0 { return false } if apiTime == 0 { diff --git a/core/app/auth/auth.go b/core/app/auth/auth.go index d8cec0d52586..2c0f859b850b 100644 --- a/core/app/auth/auth.go +++ b/core/app/auth/auth.go @@ -103,15 +103,19 @@ func VerifyMFALogin(c *gin.Context, sessionID, code, entrance string) (string, s if session.Entrance != entrance { return "", "", buserr.New("ErrEntrance") } - mfaSecret, err := settingRepo.Get(repo.WithByKey("MFASecret")) + mfaSecret, err := settingRepo.GetValueByKey("MFASecret") if err != nil { return "", "", err } - mfaInterval, err := settingRepo.Get(repo.WithByKey("MFAInterval")) + mfaInterval, err := settingRepo.GetValueByKey("MFAInterval") if err != nil { return "", "", err } - if !mfa.ValidCode(code, mfaInterval.Value, mfaSecret.Value) { + interval, err := strconv.Atoi(mfaInterval) + if err != nil { + return "", "", err + } + if !mfa.ValidCode(interval, code, mfaSecret) { return "", "ErrMFA", nil } mfaSessions.Delete(sessionID) @@ -194,13 +198,13 @@ func LoadMFA(req dto.MfaRequest) (mfa.Otp, error) { return otp, nil } func MFABind(req dto.MfaCredential) error { - success := mfa.ValidCode(req.Code, req.Interval, req.Secret) + success := mfa.ValidCode(req.Interval, req.Code, req.Secret) if !success { return errors.New("code is not valid") } settingRepo := repo.NewISettingRepo() - if err := settingRepo.Update("MFAInterval", req.Interval); err != nil { + if err := settingRepo.Update("MFAInterval", strconv.Itoa(req.Interval)); err != nil { return err } if err := settingRepo.Update("MFAStatus", constant.StatusEnable); err != nil { @@ -227,6 +231,8 @@ func GetCurrentUserInfo() (*dto.CurrentUserInfo, error) { } delete(stringSettingMap, "SessionTimeout") delete(stringSettingMap, "ExpirationDays") + delete(stringSettingMap, "MFAInterval") + delete(stringSettingMap, "ApiKeyValidityTime") arr, err := json.Marshal(stringSettingMap) if err != nil { return nil, err @@ -236,6 +242,8 @@ func GetCurrentUserInfo() (*dto.CurrentUserInfo, error) { } info.SessionTimeout, _ = strconv.Atoi(settingMap["SessionTimeout"]) info.ExpirationDays, _ = strconv.Atoi(settingMap["ExpirationDays"]) + info.MFAInterval, _ = strconv.Atoi(settingMap["MFAInterval"]) + info.ApiKeyValidityTime, _ = strconv.Atoi(settingMap["ApiKeyValidityTime"]) info.Name = settingMap["UserName"] return &info, nil } @@ -296,7 +304,7 @@ func UpdateApiConfig(req dto.ApiInterfaceConfig) error { if err := settingRepo.UpdateOrCreate("IpWhiteList", req.IpWhiteList); err != nil { return err } - if err := settingRepo.UpdateOrCreate("ApiKeyValidityTime", req.ApiKeyValidityTime); err != nil { + if err := settingRepo.UpdateOrCreate("ApiKeyValidityTime", strconv.Itoa(req.ApiKeyValidityTime)); err != nil { return err } return nil diff --git a/core/app/dto/auth.go b/core/app/dto/auth.go index b081f5172f88..02eee5471323 100644 --- a/core/app/dto/auth.go +++ b/core/app/dto/auth.go @@ -18,17 +18,6 @@ type PasskeyBeginResponse struct { PublicKey interface{} `json:"publicKey"` } -type MfaRequest struct { - Title string `json:"title" validate:"required"` - Interval int `json:"interval" validate:"required"` -} - -type MfaCredential struct { - Secret string `json:"secret" validate:"required"` - Code string `json:"code" validate:"required"` - Interval string `json:"interval" validate:"required"` -} - type Login struct { Name string `json:"name" validate:"required"` Password string `json:"password" validate:"required"` @@ -37,11 +26,6 @@ type Login struct { Language string `json:"language" validate:"required,oneof=zh en 'zh-Hant' ko ja ru ms 'pt-BR' tr 'es-ES'"` } -type MFALogin struct { - SessionID string `json:"sessionId" validate:"required"` - Code string `json:"code" validate:"required"` -} - type SystemSetting struct { IsDemo bool `json:"isDemo"` Language string `json:"language"` @@ -52,11 +36,35 @@ type PasskeyID struct { ID string `json:"id" validate:"required"` } +// mfa +type MFALogin struct { + SessionID string `json:"sessionId" validate:"required"` + Code string `json:"code" validate:"required"` +} + +type MfaRequest struct { + Title string `json:"title" validate:"required"` + Interval int `json:"interval" validate:"required"` +} + +type MfaCredential struct { + Secret string `json:"secret" validate:"required"` + Code string `json:"code" validate:"required"` + Interval int `json:"interval" validate:"required"` +} + +type ApiInterfaceConfig struct { + ApiInterfaceStatus string `json:"apiInterfaceStatus"` + ApiKey string `json:"apiKey"` + IpWhiteList string `json:"ipWhiteList"` + ApiKeyValidityTime int `json:"apiKeyValidityTime"` +} + type CurrentUserInfo struct { Name string `json:"name"` SessionTimeout int `json:"sessionTimeout"` MFAStatus string `json:"mfaStatus"` - MFAInterval string `json:"mfaInterval"` + MFAInterval int `json:"mfaInterval"` ExpirationDays int `json:"expirationDays"` ExpirationTime string `json:"expirationTime"` ComplexitySetting string `json:"complexitySetting"` @@ -64,7 +72,7 @@ type CurrentUserInfo struct { ApiInterfaceStatus string `json:"apiInterfaceStatus"` ApiKey string `json:"apiKey"` IpWhiteList string `json:"ipWhiteList"` - ApiKeyValidityTime string `json:"apiKeyValidityTime"` + ApiKeyValidityTime int `json:"apiKeyValidityTime"` } type CurrentUserUpdate struct { Name string `json:"name" validate:"required"` diff --git a/core/app/dto/setting.go b/core/app/dto/setting.go index 087913a44768..f423d09e1ee5 100644 --- a/core/app/dto/setting.go +++ b/core/app/dto/setting.go @@ -235,13 +235,6 @@ type MenuLabelSort struct { Sort int `json:"sort"` } -type ApiInterfaceConfig struct { - ApiInterfaceStatus string `json:"apiInterfaceStatus"` - ApiKey string `json:"apiKey"` - IpWhiteList string `json:"ipWhiteList"` - ApiKeyValidityTime string `json:"apiKeyValidityTime"` -} - type TerminalInfo struct { LineHeight string `json:"lineHeight"` LetterSpacing string `json:"letterSpacing"` diff --git a/core/constant/common.go b/core/constant/common.go index eb0fd83afc05..15d3e80b220a 100644 --- a/core/constant/common.go +++ b/core/constant/common.go @@ -181,8 +181,9 @@ var WebUrlMap = map[string]struct{}{ "/xpack/cluster/postgres": {}, "/xpack/cluster/redis": {}, - "/enterprise/users/list": {}, - "/enterprise/users/roles": {}, + "/enterprise/users/list": {}, + "/enterprise/users/roles": {}, + "/enterprise/license-required": {}, } var DynamicRoutes = []string{ diff --git a/core/utils/mfa/mfa.go b/core/utils/mfa/mfa.go index cdd09cb29b8f..88b2740ac8ec 100644 --- a/core/utils/mfa/mfa.go +++ b/core/utils/mfa/mfa.go @@ -3,10 +3,8 @@ package mfa import ( "bytes" "encoding/base64" - "strconv" "time" - "github.com/1Panel-dev/1Panel/core/global" "github.com/skip2/go-qrcode" "github.com/xlzd/gotp" ) @@ -32,12 +30,7 @@ func GetOtp(username, title string, interval int) (otp Otp, err error) { return } -func ValidCode(code, intervalStr, secret string) bool { - interval, err := strconv.Atoi(intervalStr) - if err != nil { - global.LOG.Errorf("type conversion failed, err: %v", err) - return false - } +func ValidCode(interval int, code, secret string) bool { totp := gotp.NewTOTP(secret, 6, interval, nil) now := time.Now().Unix() prevTime := now - int64(interval) diff --git a/frontend/src/api/interface/auth.ts b/frontend/src/api/interface/auth.ts index 63a87c2d5661..78031774c21f 100644 --- a/frontend/src/api/interface/auth.ts +++ b/frontend/src/api/interface/auth.ts @@ -82,13 +82,13 @@ export namespace Login { export interface MFABind { secret: string; code: string; - interval: string; + interval: number; } export interface ApiConfig { apiInterfaceStatus: string; apiKey: string; ipWhiteList: string; - apiKeyValidityTime: string; + apiKeyValidityTime: number; } export interface PasswordUpdate { oldPassword: string; diff --git a/frontend/src/layout/components/Sidebar/components/Collapse.vue b/frontend/src/layout/components/Sidebar/components/Collapse.vue index 7572602a8e29..dba5de50d322 100644 --- a/frontend/src/layout/components/Sidebar/components/Collapse.vue +++ b/frontend/src/layout/components/Sidebar/components/Collapse.vue @@ -1,7 +1,6 @@ - +
+ + + {{ $t('commons.button.view') }} + +
{{ $t('setting.apiInterfaceHelper') }} @@ -356,7 +366,7 @@ {{ $t('setting.ipWhiteListHelper') }} - + @@ -667,6 +677,11 @@ const handleApiDialogClose = () => { form.apiInterfaceStatus = savedApiStatus.value; }; +const openApiDetail = async () => { + await ensureApiKey(); + apiDialogOpen.value = true; +}; + const openPasskeyDrawer = async () => { const settingRes = await loadPasskeySettingInfo(); hasBindDomain.value = !!settingRes?.data.bindDomain?.trim().length; @@ -862,7 +877,7 @@ const onBindMFA = async (formEl: FormInstance | undefined) => { const param = { code: mfaForm.code, secret: mfaForm.secret, - interval: mfaForm.interval + '', + interval: mfaForm.interval, }; loading.value = true; await bindMFA(param) @@ -887,7 +902,7 @@ const onSaveApi = async (formEl: FormInstance | undefined) => { apiKey: form.apiKey, ipWhiteList: form.ipWhiteList, apiInterfaceStatus: form.apiInterfaceStatus, - apiKeyValidityTime: form.apiKeyValidityTime + '', + apiKeyValidityTime: form.apiKeyValidityTime, }; loading.value = true; await updateApiConfig(param) @@ -981,7 +996,7 @@ const handleApi = async () => { apiKey: form.apiKey, ipWhiteList: form.ipWhiteList, apiInterfaceStatus: form.apiInterfaceStatus, - apiKeyValidityTime: form.apiKeyValidityTime + '', + apiKeyValidityTime: form.apiKeyValidityTime, }; await updateApiConfig(param) .then(() => { @@ -1024,6 +1039,12 @@ defineExpose({ border-top: 1px solid var(--el-border-color-lighter); } +.setting-action-row { + display: flex; + align-items: center; + gap: 12px; +} + .api-key-input { width: calc(100% - 125px); }