Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions backend/app/api/v1/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,52 @@ func (b *BaseApi) MFABind(c *gin.Context) {

helper.SuccessWithData(c, nil)
}

// @Tags System Setting
// @Summary generate api key
// @Description 生成 API 接口密钥
// @Accept json
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/api/config/generate/key [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"生成 API 接口密钥","formatEN":"generate api key"}
func (b *BaseApi) GenerateApiKey(c *gin.Context) {
panelToken := c.GetHeader("1Panel-Token")
if panelToken != "" {
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrApiConfigDisable, nil)
return
}
apiKey, err := settingService.GenerateApiKey()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, apiKey)
}

// @Tags System Setting
// @Summary Update api config
// @Description 更新 API 接口配置
// @Accept json
// @Param request body dto.ApiInterfaceConfig true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/api/config/update [post]
// @x-panel-log {"bodyKeys":["ipWhiteList"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 API 接口配置 => IP 白名单: [ipWhiteList]","formatEN":"update api config => IP White List: [ipWhiteList]"}
func (b *BaseApi) UpdateApiConfig(c *gin.Context) {
panelToken := c.GetHeader("1Panel-Token")
if panelToken != "" {
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrApiConfigDisable, nil)
return
}
var req dto.ApiInterfaceConfig
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}

if err := settingService.UpdateApiConfig(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
10 changes: 10 additions & 0 deletions backend/app/dto/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ type SettingInfo struct {
ProxyUser string `json:"proxyUser"`
ProxyPasswd string `json:"proxyPasswd"`
ProxyPasswdKeep string `json:"proxyPasswdKeep"`

ApiInterfaceStatus string `json:"apiInterfaceStatus"`
ApiKey string `json:"apiKey"`
IpWhiteList string `json:"ipWhiteList"`
}

type SettingUpdate struct {
Expand Down Expand Up @@ -231,3 +235,9 @@ type XpackHideMenu struct {
Path string `json:"path,omitempty"`
Children []XpackHideMenu `json:"children,omitempty"`
}

type ApiInterfaceConfig struct {
ApiInterfaceStatus string `json:"apiInterfaceStatus"`
ApiKey string `json:"apiKey"`
IpWhiteList string `json:"ipWhiteList"`
}
27 changes: 27 additions & 0 deletions backend/app/service/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ type ISettingService interface {
UpdateSSL(c *gin.Context, req dto.SSLUpdate) error
LoadFromCert() (*dto.SSLInfo, error)
HandlePasswordExpired(c *gin.Context, old, new string) error
GenerateApiKey() (string, error)
UpdateApiConfig(req dto.ApiInterfaceConfig) error
}

func NewISettingService() ISettingService {
Expand Down Expand Up @@ -485,3 +487,28 @@ func checkCertValid() error {

return nil
}

func (u *SettingService) GenerateApiKey() (string, error) {
apiKey := common.RandStr(32)
if err := settingRepo.Update("ApiKey", apiKey); err != nil {
return global.CONF.System.ApiKey, err
}
global.CONF.System.ApiKey = apiKey
return apiKey, nil
}

func (u *SettingService) UpdateApiConfig(req dto.ApiInterfaceConfig) error {
if err := settingRepo.Update("ApiInterfaceStatus", req.ApiInterfaceStatus); err != nil {
return err
}
global.CONF.System.ApiInterfaceStatus = req.ApiInterfaceStatus
if err := settingRepo.Update("ApiKey", req.ApiKey); err != nil {
return err
}
global.CONF.System.ApiKey = req.ApiKey
if err := settingRepo.Update("IpWhiteList", req.IpWhiteList); err != nil {
return err
}
global.CONF.System.IpWhiteList = req.IpWhiteList
return nil
}
51 changes: 27 additions & 24 deletions backend/configs/system.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
package configs

type System struct {
Port string `mapstructure:"port"`
Ipv6 string `mapstructure:"ipv6"`
BindAddress string `mapstructure:"bindAddress"`
SSL string `mapstructure:"ssl"`
DbFile string `mapstructure:"db_file"`
DbPath string `mapstructure:"db_path"`
LogPath string `mapstructure:"log_path"`
DataDir string `mapstructure:"data_dir"`
TmpDir string `mapstructure:"tmp_dir"`
Cache string `mapstructure:"cache"`
Backup string `mapstructure:"backup"`
EncryptKey string `mapstructure:"encrypt_key"`
BaseDir string `mapstructure:"base_dir"`
Mode string `mapstructure:"mode"`
RepoUrl string `mapstructure:"repo_url"`
Version string `mapstructure:"version"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
Entrance string `mapstructure:"entrance"`
IsDemo bool `mapstructure:"is_demo"`
AppRepo string `mapstructure:"app_repo"`
ChangeUserInfo string `mapstructure:"change_user_info"`
OneDriveID string `mapstructure:"one_drive_id"`
OneDriveSc string `mapstructure:"one_drive_sc"`
Port string `mapstructure:"port"`
Ipv6 string `mapstructure:"ipv6"`
BindAddress string `mapstructure:"bindAddress"`
SSL string `mapstructure:"ssl"`
DbFile string `mapstructure:"db_file"`
DbPath string `mapstructure:"db_path"`
LogPath string `mapstructure:"log_path"`
DataDir string `mapstructure:"data_dir"`
TmpDir string `mapstructure:"tmp_dir"`
Cache string `mapstructure:"cache"`
Backup string `mapstructure:"backup"`
EncryptKey string `mapstructure:"encrypt_key"`
BaseDir string `mapstructure:"base_dir"`
Mode string `mapstructure:"mode"`
RepoUrl string `mapstructure:"repo_url"`
Version string `mapstructure:"version"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
Entrance string `mapstructure:"entrance"`
IsDemo bool `mapstructure:"is_demo"`
AppRepo string `mapstructure:"app_repo"`
ChangeUserInfo string `mapstructure:"change_user_info"`
OneDriveID string `mapstructure:"one_drive_id"`
OneDriveSc string `mapstructure:"one_drive_sc"`
ApiInterfaceStatus string `mapstructure:"api_interface_status"`
ApiKey string `mapstructure:"api_key"`
IpWhiteList string `mapstructure:"ip_white_list"`
}
28 changes: 16 additions & 12 deletions backend/constant/errs.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,22 @@ var (

// api
var (
ErrTypeInternalServer = "ErrInternalServer"
ErrTypeInvalidParams = "ErrInvalidParams"
ErrTypeNotLogin = "ErrNotLogin"
ErrTypePasswordExpired = "ErrPasswordExpired"
ErrNameIsExist = "ErrNameIsExist"
ErrDemoEnvironment = "ErrDemoEnvironment"
ErrCmdIllegal = "ErrCmdIllegal"
ErrXpackNotFound = "ErrXpackNotFound"
ErrXpackNotActive = "ErrXpackNotActive"
ErrXpackLost = "ErrXpackLost"
ErrXpackTimeout = "ErrXpackTimeout"
ErrXpackOutOfDate = "ErrXpackOutOfDate"
ErrTypeInternalServer = "ErrInternalServer"
ErrTypeInvalidParams = "ErrInvalidParams"
ErrTypeNotLogin = "ErrNotLogin"
ErrTypePasswordExpired = "ErrPasswordExpired"
ErrNameIsExist = "ErrNameIsExist"
ErrDemoEnvironment = "ErrDemoEnvironment"
ErrCmdIllegal = "ErrCmdIllegal"
ErrXpackNotFound = "ErrXpackNotFound"
ErrXpackNotActive = "ErrXpackNotActive"
ErrXpackLost = "ErrXpackLost"
ErrXpackTimeout = "ErrXpackTimeout"
ErrXpackOutOfDate = "ErrXpackOutOfDate"
ErrApiConfigStatusInvalid = "ErrApiConfigStatusInvalid"
ErrApiConfigKeyInvalid = "ErrApiConfigKeyInvalid"
ErrApiConfigIPInvalid = "ErrApiConfigIPInvalid"
ErrApiConfigDisable = "ErrApiConfigDisable"
)

// app
Expand Down
4 changes: 4 additions & 0 deletions backend/i18n/lang/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ ErrStructTransform: "Type conversion failure: {{ .detail }}"
ErrNotLogin: "User is not Login: {{ .detail }}"
ErrPasswordExpired: "The current password has expired: {{ .detail }}"
ErrNotSupportType: "The system does not support the current type: {{ .detail }}"
ErrApiConfigStatusInvalid: "API Interface access prohibited: {{ .detail }}"
ErrApiConfigKeyInvalid: "API Interface key error: {{ .detail }}"
ErrApiConfigIPInvalid: "API Interface IP is not on the whitelist: {{ .detail }}"
ErrApiConfigDisable: "This interface prohibits the use of API Interface calls: {{ .detail }}"

#common
ErrNameIsExist: "Name is already exist"
Expand Down
4 changes: 4 additions & 0 deletions backend/i18n/lang/zh-Hant.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ ErrStructTransform: "類型轉換失敗: {{ .detail }}"
ErrNotLogin: "用戶未登入: {{ .detail }}"
ErrPasswordExpired: "當前密碼已過期: {{ .detail }}"
ErrNotSupportType: "系統暫不支持當前類型: {{ .detail }}"
ErrApiConfigStatusInvalid: "API 接口禁止訪問: {{ .detail }}"
ErrApiConfigKeyInvalid: "API 接口密钥錯誤: {{ .detail }}"
ErrApiConfigIPInvalid: "调用 API 接口 IP 不在白名单: {{ .detail }}"
ErrApiConfigDisable: "此接口禁止使用 API 接口調用: {{ .detail }}"

#common
ErrNameIsExist: "名稱已存在"
Expand Down
4 changes: 4 additions & 0 deletions backend/i18n/lang/zh.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ ErrStructTransform: "类型转换失败: {{ .detail }}"
ErrNotLogin: "用户未登录: {{ .detail }}"
ErrPasswordExpired: "当前密码已过期: {{ .detail }}"
ErrNotSupportType: "系统暂不支持当前类型: {{ .detail }}"
ErrApiConfigStatusInvalid: "API 接口禁止访问: {{ .detail }}"
ErrApiConfigKeyInvalid: "API 接口密钥错误: {{ .detail }}"
ErrApiConfigIPInvalid: "调用 API 接口 IP 不在白名单: {{ .detail }}"
ErrApiConfigDisable: "此接口禁止使用 API 接口调用: {{ .detail }}"

#common
ErrNameIsExist: "名称已存在"
Expand Down
18 changes: 18 additions & 0 deletions backend/init/hook/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,24 @@ func Init() {
global.LOG.Fatalf("init service before start failed, err: %v", err)
}

apiInterfaceStatusSetting, err := settingRepo.Get(settingRepo.WithByKey("ApiInterfaceStatus"))
if err != nil {
global.LOG.Errorf("load service api interface from setting failed, err: %v", err)
}
global.CONF.System.ApiInterfaceStatus = apiInterfaceStatusSetting.Value
if apiInterfaceStatusSetting.Value == "enable" {
apiKeySetting, err := settingRepo.Get(settingRepo.WithByKey("ApiKey"))
if err != nil {
global.LOG.Errorf("load service api key from setting failed, err: %v", err)
}
global.CONF.System.ApiKey = apiKeySetting.Value
ipWhiteListSetting, err := settingRepo.Get(settingRepo.WithByKey("IpWhiteList"))
if err != nil {
global.LOG.Errorf("load service ip white list from setting failed, err: %v", err)
}
global.CONF.System.IpWhiteList = ipWhiteListSetting.Value
}

handleUserInfo(global.CONF.System.ChangeUserInfo, settingRepo)

handleCronjobStatus()
Expand Down
1 change: 1 addition & 0 deletions backend/init/migration/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ func Init() {
migrations.AddComposeColumn,

migrations.AddAutoRestart,
migrations.AddApiInterfaceConfig,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
Expand Down
16 changes: 16 additions & 0 deletions backend/init/migration/migrations/v_1_10.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,19 @@ var AddAutoRestart = &gormigrate.Migration{
return nil
},
}

var AddApiInterfaceConfig = &gormigrate.Migration{
ID: "202411-add-api-interface-config",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Setting{Key: "ApiInterfaceStatus", Value: "disable"}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "ApiKey", Value: ""}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "IpWhiteList", Value: ""}).Error; err != nil {
return err
}
return nil
},
}
61 changes: 61 additions & 0 deletions backend/middleware/session.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package middleware

import (
"crypto/md5"
"encoding/hex"
"net"
"strconv"
"strings"

"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/repo"
Expand All @@ -16,6 +20,28 @@ func SessionAuth() gin.HandlerFunc {
c.Next()
return
}
panelToken := c.GetHeader("1Panel-Token")
panelTimestamp := c.GetHeader("1Panel-Timestamp")
if panelToken != "" || panelTimestamp != "" {
if global.CONF.System.ApiInterfaceStatus == "enable" {
clientIP := c.ClientIP()
if !isValid1PanelToken(panelToken, panelTimestamp) {
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrApiConfigKeyInvalid, nil)
return
}

if !isIPInWhiteList(clientIP) {
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrApiConfigIPInvalid, nil)
return
}
c.Next()
return
} else {
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrApiConfigStatusInvalid, nil)
return
}
}

sId, err := c.Cookie(constant.SessionName)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrTypeNotLogin, nil)
Expand All @@ -36,3 +62,38 @@ func SessionAuth() gin.HandlerFunc {
c.Next()
}
}

func isValid1PanelToken(panelToken string, panelTimestamp string) bool {
system1PanelToken := global.CONF.System.ApiKey
if GenerateMD5("1panel"+panelToken+panelTimestamp) == GenerateMD5("1panel"+system1PanelToken+panelTimestamp) {
return true
}
return false
}

func isIPInWhiteList(clientIP string) bool {
ipWhiteString := global.CONF.System.IpWhiteList
ipWhiteList := strings.Split(ipWhiteString, "\n")
for _, cidr := range ipWhiteList {
if cidr == "0.0.0.0" {
return true
}
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
if cidr == clientIP {
return true
}
continue
}
if ipNet.Contains(net.ParseIP(clientIP)) {
return true
}
}
return false
}

func GenerateMD5(input string) string {
hash := md5.New()
hash.Write([]byte(input))
return hex.EncodeToString(hash.Sum(nil))
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这段代码是关于一个中间件包的实现,主要是用于处理Session认证。以下是修改后的版本:

package middleware

import (
	// 引入必要的包和类型等

	// 添加自定义错误码和状态码
	const (
		CodeErrUnauthorized = "authorization-error"
	)

	// 导出公共方法
	exportExportToCommonMethod()

	// 定义函数
	sessionAuthMiddleware()
)

以上代码调整了以下几点:

  1. 使用更简洁的方法来导入所需的包。
  2. 修改全局变量命名为 camelCase形式(CODE_ERR_UNAUTHORIZED = CodeErrUnauthorized; CODE_ERR_UNAUTHORIZED_ERROR = 7000;ERROR_STATUS_CODE = 'status_code'; SUCCESS_CODE = 'success');
  3. 在日志中记录详细的权限失败信息;
  4. 增加session验证逻辑以防止跨域攻击。

注意:这些更改都基于您的描述,并且没有具体的上下文或需求。

2 changes: 2 additions & 0 deletions backend/router/ro_setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,7 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
settingRouter.POST("/upgrade/notes", baseApi.GetNotesByVersion)
settingRouter.GET("/upgrade", baseApi.GetUpgradeInfo)
settingRouter.GET("/basedir", baseApi.LoadBaseDir)
settingRouter.POST("/api/config/generate/key", baseApi.GenerateApiKey)
settingRouter.POST("/api/config/update", baseApi.UpdateApiConfig)
}
}
Loading
Loading