1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 08:19:15 +08:00

fix: 解决越权下载文件的问题 (#1813)

This commit is contained in:
ssongliu 2023-08-02 22:36:37 +08:00 committed by GitHub
parent 202a2ad7ae
commit f6b84d384e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 111 additions and 198 deletions

View File

@ -246,7 +246,8 @@ func (b *BaseApi) TargetDownload(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
} }
helper.SuccessWithData(c, filePath)
c.File(filePath)
} }
// @Tags Cronjob // @Tags Cronjob

View File

@ -544,28 +544,6 @@ func (b *BaseApi) DownloadChunkFiles(c *gin.Context) {
} }
} }
// @Tags File
// @Summary Download file with path
// @Description 下载指定文件
// @Accept json
// @Param request body dto.FilePath true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/download/bypath [post]
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"下载文件 [path]","formatEN":"Download file [path]"}
func (b *BaseApi) DownloadFile(c *gin.Context) {
var req dto.FilePath
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
}
c.File(req.Path)
}
// @Tags File // @Tags File
// @Summary Load file size // @Summary Load file size
// @Description 获取文件夹大小 // @Description 获取文件夹大小

View File

@ -3,6 +3,8 @@ package v1
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"path"
"strconv" "strconv"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" "github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
@ -134,6 +136,22 @@ func (b *BaseApi) LoadFromCert(c *gin.Context) {
helper.SuccessWithData(c, info) helper.SuccessWithData(c, info)
} }
// @Tags System Setting
// @Summary Download system cert
// @Description 下载证书
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/ssl/download [post]
func (b *BaseApi) DownloadSSL(c *gin.Context) {
pathItem := path.Join(global.CONF.System.BaseDir, "1panel/secret/server.crt")
if _, err := os.Stat(pathItem); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
c.File(pathItem)
}
// @Tags System Setting // @Tags System Setting
// @Summary Update system port // @Summary Update system port
// @Description 更新系统端口 // @Description 更新系统端口

View File

@ -141,12 +141,8 @@ func (u *CronjobService) Download(down dto.CronjobDownload) (string, error) {
if record.ID == 0 { if record.ID == 0 {
return "", constant.ErrRecordNotFound return "", constant.ErrRecordNotFound
} }
cronjob, _ := cronjobRepo.Get(commonRepo.WithByID(record.CronjobID))
if cronjob.ID == 0 {
return "", constant.ErrRecordNotFound
}
backup, _ := backupRepo.Get(commonRepo.WithByID(down.BackupAccountID)) backup, _ := backupRepo.Get(commonRepo.WithByID(down.BackupAccountID))
if cronjob.ID == 0 { if backup.ID == 0 {
return "", constant.ErrRecordNotFound return "", constant.ErrRecordNotFound
} }
if backup.Type == "LOCAL" || record.FromLocal { if backup.Type == "LOCAL" || record.FromLocal {
@ -155,16 +151,18 @@ func (u *CronjobService) Download(down dto.CronjobDownload) (string, error) {
} }
return record.File, nil return record.File, nil
} }
tempPath := fmt.Sprintf("%s/download/%s", constant.DataDir, record.File)
if _, err := os.Stat(tempPath); err != nil && os.IsNotExist(err) {
client, err := NewIBackupService().NewClient(&backup) client, err := NewIBackupService().NewClient(&backup)
if err != nil { if err != nil {
return "", err return "", err
} }
tempPath := fmt.Sprintf("%s/download/%s", constant.DataDir, record.File)
_ = os.MkdirAll(path.Dir(tempPath), os.ModePerm) _ = os.MkdirAll(path.Dir(tempPath), os.ModePerm)
isOK, err := client.Download(record.File, tempPath) isOK, err := client.Download(record.File, tempPath)
if !isOK || err != nil { if !isOK || err != nil {
return "", err return "", err
} }
}
return tempPath, nil return tempPath, nil
} }

View File

@ -370,7 +370,7 @@ func loadInfoFromCert() (*dto.SSLInfo, error) {
return &dto.SSLInfo{ return &dto.SSLInfo{
Domain: strings.Join(domains, ","), Domain: strings.Join(domains, ","),
Timeout: certObj.NotAfter.Format("2006-01-02 15:04:05"), Timeout: certObj.NotAfter.Format("2006-01-02 15:04:05"),
RootPath: global.CONF.System.BaseDir + "/1panel/secret/server.crt", RootPath: path.Join(global.CONF.System.BaseDir, "1panel/secret/server.crt"),
}, nil }, nil
} }

View File

@ -33,7 +33,6 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) {
fileRouter.POST("/wget", baseApi.WgetFile) fileRouter.POST("/wget", baseApi.WgetFile)
fileRouter.POST("/move", baseApi.MoveFile) fileRouter.POST("/move", baseApi.MoveFile)
fileRouter.GET("/download", baseApi.Download) fileRouter.GET("/download", baseApi.Download)
fileRouter.POST("/download/bypath", baseApi.DownloadFile)
fileRouter.POST("/chunkdownload", baseApi.DownloadChunkFiles) fileRouter.POST("/chunkdownload", baseApi.DownloadChunkFiles)
fileRouter.POST("/size", baseApi.Size) fileRouter.POST("/size", baseApi.Size)
fileRouter.GET("/ws", baseApi.Ws) fileRouter.GET("/ws", baseApi.Ws)

View File

@ -25,6 +25,7 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
settingRouter.POST("/port/update", baseApi.UpdatePort) settingRouter.POST("/port/update", baseApi.UpdatePort)
settingRouter.POST("/ssl/update", baseApi.UpdateSSL) settingRouter.POST("/ssl/update", baseApi.UpdateSSL)
settingRouter.GET("/ssl/info", baseApi.LoadFromCert) settingRouter.GET("/ssl/info", baseApi.LoadFromCert)
settingRouter.POST("/ssl/download", baseApi.DownloadSSL)
settingRouter.POST("/password/update", baseApi.UpdatePassword) settingRouter.POST("/password/update", baseApi.UpdatePassword)
settingRouter.GET("/time/option", baseApi.LoadTimeZone) settingRouter.GET("/time/option", baseApi.LoadTimeZone)
settingRouter.POST("/time/sync", baseApi.SyncTime) settingRouter.POST("/time/sync", baseApi.SyncTime)

View File

@ -5093,48 +5093,6 @@ const docTemplate = `{
} }
} }
}, },
"/files/download/bypath": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "下载指定文件",
"consumes": [
"application/json"
],
"tags": [
"File"
],
"summary": "Download file with path",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.FilePath"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"path"
],
"formatEN": "Download file [path]",
"formatZH": "下载文件 [path]",
"paramKeys": []
}
}
},
"/files/mode": { "/files/mode": {
"post": { "post": {
"security": [ "security": [
@ -8823,6 +8781,25 @@ const docTemplate = `{
} }
} }
}, },
"/settings/ssl/download": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "下载证书",
"tags": [
"System Setting"
],
"summary": "Download system cert",
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/settings/ssl/info": { "/settings/ssl/info": {
"get": { "get": {
"security": [ "security": [
@ -12520,17 +12497,6 @@ const docTemplate = `{
} }
} }
}, },
"dto.FilePath": {
"type": "object",
"required": [
"path"
],
"properties": {
"path": {
"type": "string"
}
}
},
"dto.FirewallBaseInfo": { "dto.FirewallBaseInfo": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -5086,48 +5086,6 @@
} }
} }
}, },
"/files/download/bypath": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "下载指定文件",
"consumes": [
"application/json"
],
"tags": [
"File"
],
"summary": "Download file with path",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.FilePath"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"path"
],
"formatEN": "Download file [path]",
"formatZH": "下载文件 [path]",
"paramKeys": []
}
}
},
"/files/mode": { "/files/mode": {
"post": { "post": {
"security": [ "security": [
@ -8816,6 +8774,25 @@
} }
} }
}, },
"/settings/ssl/download": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "下载证书",
"tags": [
"System Setting"
],
"summary": "Download system cert",
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/settings/ssl/info": { "/settings/ssl/info": {
"get": { "get": {
"security": [ "security": [
@ -12513,17 +12490,6 @@
} }
} }
}, },
"dto.FilePath": {
"type": "object",
"required": [
"path"
],
"properties": {
"path": {
"type": "string"
}
}
},
"dto.FirewallBaseInfo": { "dto.FirewallBaseInfo": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -747,13 +747,6 @@ definitions:
- fileName - fileName
- source - source
type: object type: object
dto.FilePath:
properties:
path:
type: string
required:
- path
type: object
dto.FirewallBaseInfo: dto.FirewallBaseInfo:
properties: properties:
name: name:
@ -7084,33 +7077,6 @@ paths:
formatEN: Download file [name] formatEN: Download file [name]
formatZH: 下载文件 [name] formatZH: 下载文件 [name]
paramKeys: [] paramKeys: []
/files/download/bypath:
post:
consumes:
- application/json
description: 下载指定文件
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.FilePath'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Download file with path
tags:
- File
x-panel-log:
BeforeFuntions: []
bodyKeys:
- path
formatEN: Download file [path]
formatZH: 下载文件 [path]
paramKeys: []
/files/mode: /files/mode:
post: post:
consumes: consumes:
@ -9454,6 +9420,17 @@ paths:
summary: Page system snapshot summary: Page system snapshot
tags: tags:
- System Setting - System Setting
/settings/ssl/download:
post:
description: 下载证书
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Download system cert
tags:
- System Setting
/settings/ssl/info: /settings/ssl/info:
get: get:
description: 获取证书信息 description: 获取证书信息

View File

@ -38,8 +38,11 @@ export const updateStatus = (params: Cronjob.UpdateStatus) => {
return http.post(`cronjobs/status`, params); return http.post(`cronjobs/status`, params);
}; };
export const download = (params: Cronjob.Download) => { export const downloadRecordCheck = (params: Cronjob.Download) => {
return http.post<string>(`cronjobs/download`, params); return http.post<string>(`cronjobs/download`, params, 40000);
};
export const downloadRecord = (params: Cronjob.Download) => {
return http.download<BlobPart>(`cronjobs/download`, params, { responseType: 'blob', timeout: 40000 });
}; };
export const handleOnce = (id: number) => { export const handleOnce = (id: number) => {

View File

@ -79,10 +79,6 @@ export const DownloadFile = (params: File.FileDownload) => {
return http.download<BlobPart>('files/download', params, { responseType: 'blob', timeout: 20000 }); return http.download<BlobPart>('files/download', params, { responseType: 'blob', timeout: 20000 });
}; };
export const DownloadByPath = (path: string) => {
return http.download<BlobPart>('files/download/bypath', { path: path }, { responseType: 'blob', timeout: 40000 });
};
export const ComputeDirSize = (params: File.DirSizeReq) => { export const ComputeDirSize = (params: File.DirSizeReq) => {
return http.post<File.DirSizeRes>('files/size', params); return http.post<File.DirSizeRes>('files/size', params);
}; };

View File

@ -30,6 +30,9 @@ export const updateSSL = (param: Setting.SSLUpdate) => {
export const loadSSLInfo = () => { export const loadSSLInfo = () => {
return http.get<Setting.SSLInfo>(`/settings/ssl/info`); return http.get<Setting.SSLInfo>(`/settings/ssl/info`);
}; };
export const downloadSSL = () => {
return http.download<any>(`settings/ssl/download`);
};
export const handleExpired = (param: Setting.PasswordUpdate) => { export const handleExpired = (param: Setting.PasswordUpdate) => {
return http.post(`/settings/expired/handle`, param); return http.post(`/settings/expired/handle`, param);

View File

@ -358,11 +358,18 @@
import { onBeforeUnmount, reactive, ref } from 'vue'; import { onBeforeUnmount, reactive, ref } from 'vue';
import { Cronjob } from '@/api/interface/cronjob'; import { Cronjob } from '@/api/interface/cronjob';
import { loadZero } from '@/utils/util'; import { loadZero } from '@/utils/util';
import { searchRecords, download, handleOnce, updateStatus, cleanRecords, getRecordLog } from '@/api/modules/cronjob'; import {
searchRecords,
downloadRecord,
handleOnce,
updateStatus,
cleanRecords,
getRecordLog,
downloadRecordCheck,
} from '@/api/modules/cronjob';
import { dateFormat } from '@/utils/util'; import { dateFormat } from '@/utils/util';
import i18n from '@/lang'; import i18n from '@/lang';
import { ElMessageBox } from 'element-plus'; import { ElMessageBox } from 'element-plus';
import { DownloadByPath } from '@/api/modules/files';
import { Codemirror } from 'vue-codemirror'; import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript'; import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark'; import { oneDark } from '@codemirror/theme-one-dark';
@ -562,8 +569,8 @@ const onDownload = async (record: any, backupID: number) => {
recordID: record.id, recordID: record.id,
backupAccountID: backupID, backupAccountID: backupID,
}; };
await download(params).then(async (res) => { await downloadRecordCheck(params).then(async () => {
const file = await DownloadByPath(res.data); const file = await downloadRecord(params);
const downloadUrl = window.URL.createObjectURL(new Blob([file])); const downloadUrl = window.URL.createObjectURL(new Blob([file]));
const a = document.createElement('a'); const a = document.createElement('a');
a.style.display = 'none'; a.style.display = 'none';

View File

@ -107,8 +107,7 @@ import { ListSSL } from '@/api/modules/website';
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import i18n from '@/lang'; import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
import { updateSSL } from '@/api/modules/setting'; import { downloadSSL, updateSSL } from '@/api/modules/setting';
import { DownloadByPath } from '@/api/modules/files';
import { Rules } from '@/global/form-rules'; import { Rules } from '@/global/form-rules';
import { ElMessageBox, FormInstance } from 'element-plus'; import { ElMessageBox, FormInstance } from 'element-plus';
import { Setting } from '@/api/interface/setting'; import { Setting } from '@/api/interface/setting';
@ -178,7 +177,7 @@ const changeSSl = (sslid: number) => {
}; };
const onDownload = async () => { const onDownload = async () => {
const file = await DownloadByPath(form.rootPath); await downloadSSL().then(async (file) => {
const downloadUrl = window.URL.createObjectURL(new Blob([file])); const downloadUrl = window.URL.createObjectURL(new Blob([file]));
const a = document.createElement('a'); const a = document.createElement('a');
a.style.display = 'none'; a.style.display = 'none';
@ -186,6 +185,7 @@ const onDownload = async () => {
a.download = 'server.crt'; a.download = 'server.crt';
const event = new MouseEvent('click'); const event = new MouseEvent('click');
a.dispatchEvent(event); a.dispatchEvent(event);
});
}; };
const onSaveSSL = async (formEl: FormInstance | undefined) => { const onSaveSSL = async (formEl: FormInstance | undefined) => {