feat: normalize mfa and api config fields (#12642)

This commit is contained in:
ssongliu
2026-04-30 16:51:55 +08:00
committed by zhengkunwang223
parent 8fb8d97dcf
commit 8d44105f88
8 changed files with 86 additions and 59 deletions

View File

@@ -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 {

View File

@@ -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

View File

@@ -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"`

View File

@@ -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"`

View File

@@ -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{

View File

@@ -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)

View File

@@ -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;

View File

@@ -147,12 +147,22 @@
</el-tooltip>
</span>
</template>
<el-switch
@change="handleApi"
v-model="form.apiInterfaceStatus"
active-value="Enable"
inactive-value="Disable"
/>
<div class="setting-action-row">
<el-switch
@change="handleApi"
v-model="form.apiInterfaceStatus"
active-value="Enable"
inactive-value="Disable"
/>
<el-button
v-if="form.apiInterfaceStatus === 'Enable'"
link
type="primary"
@click="openApiDetail"
>
{{ $t('commons.button.view') }}
</el-button>
</div>
<span class="input-help">{{ $t('setting.apiInterfaceHelper') }}</span>
</el-form-item>
</el-form>
@@ -356,7 +366,7 @@
<span class="input-help">{{ $t('setting.ipWhiteListHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('setting.apiKeyValidityTime')" prop="apiKeyValidityTime">
<el-input :placeholder="$t('setting.apiKeyValidityTimeEgs')" v-model="form.apiKeyValidityTime">
<el-input :placeholder="$t('setting.apiKeyValidityTimeEgs')" v-model.number="form.apiKeyValidityTime">
<template #append>{{ $t('commons.units.minute') }}</template>
</el-input>
<span class="input-help">
@@ -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);
}