mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-18 22:22:59 +08:00
feat: PHP 网站增加禁用函数设置 (#1125)
Refs https://github.com/1Panel-dev/1Panel/issues/663
This commit is contained in:
parent
d7c08295f8
commit
f77972fa38
@ -138,8 +138,10 @@ type WebsiteDefaultUpdate struct {
|
||||
}
|
||||
|
||||
type WebsitePHPConfigUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Params map[string]string `json:"params" validate:"required"`
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Params map[string]string `json:"params"`
|
||||
Scope string `json:"scope" validate:"required"`
|
||||
DisableFunctions []string `json:"disableFunctions"`
|
||||
}
|
||||
|
||||
type WebsitePHPFileUpdate struct {
|
||||
|
@ -45,7 +45,8 @@ type WebsiteLog struct {
|
||||
}
|
||||
|
||||
type PHPConfig struct {
|
||||
Params map[string]string `json:"params"`
|
||||
Params map[string]string `json:"params"`
|
||||
DisableFunctions []string `json:"disableFunctions"`
|
||||
}
|
||||
|
||||
type NginxRewriteRes struct {
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser"
|
||||
"github.com/1Panel-dev/1Panel/cmd/server/nginx_conf"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gopkg.in/ini.v1"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
"path"
|
||||
@ -1009,7 +1010,23 @@ func (w WebsiteService) GetPHPConfig(id uint) (*response.PHPConfig, error) {
|
||||
params[matches[1]] = matches[2]
|
||||
}
|
||||
}
|
||||
return &response.PHPConfig{Params: params}, nil
|
||||
cfg, err := ini.Load(phpConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
phpConfig, err := cfg.GetSection("PHP")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disableFunctionStr := phpConfig.Key("disable_functions").Value()
|
||||
res := &response.PHPConfig{Params: params}
|
||||
if disableFunctionStr != "" {
|
||||
disableFunctions := strings.Split(disableFunctionStr, ",")
|
||||
if len(disableFunctions) > 0 {
|
||||
res.DisableFunctions = disableFunctions
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err error) {
|
||||
@ -1033,23 +1050,50 @@ func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err
|
||||
defer configFile.Close()
|
||||
|
||||
contentBytes, err := fileOp.GetContent(phpConfigPath)
|
||||
content := string(contentBytes)
|
||||
lines := strings.Split(content, "\n")
|
||||
for i, line := range lines {
|
||||
if strings.HasPrefix(line, ";") {
|
||||
continue
|
||||
}
|
||||
for key, value := range req.Params {
|
||||
pattern := "^" + regexp.QuoteMeta(key) + "\\s*=\\s*.*$"
|
||||
if matched, _ := regexp.MatchString(pattern, line); matched {
|
||||
lines[i] = key + " = " + value
|
||||
}
|
||||
}
|
||||
}
|
||||
updatedContent := strings.Join(lines, "\n")
|
||||
if err := fileOp.WriteFile(phpConfigPath, strings.NewReader(updatedContent), 0755); err != nil {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if req.Scope == "params" {
|
||||
content := string(contentBytes)
|
||||
lines := strings.Split(content, "\n")
|
||||
for i, line := range lines {
|
||||
if strings.HasPrefix(line, ";") {
|
||||
continue
|
||||
}
|
||||
for key, value := range req.Params {
|
||||
pattern := "^" + regexp.QuoteMeta(key) + "\\s*=\\s*.*$"
|
||||
if matched, _ := regexp.MatchString(pattern, line); matched {
|
||||
lines[i] = key + " = " + value
|
||||
}
|
||||
}
|
||||
}
|
||||
updatedContent := strings.Join(lines, "\n")
|
||||
if err := fileOp.WriteFile(phpConfigPath, strings.NewReader(updatedContent), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cfg, err := ini.Load(phpConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
phpConfig, err := cfg.GetSection("PHP")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.Scope == "disable_functions" {
|
||||
disable := phpConfig.Key("disable_functions")
|
||||
disable.SetValue(strings.Join(req.DisableFunctions, ","))
|
||||
if err = cfg.SaveTo(phpConfigPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.Scope == "uploadSize" {
|
||||
postMaxSize := phpConfig.Key("post_max_size")
|
||||
postMaxSize.SetValue("")
|
||||
}
|
||||
|
||||
appInstallReq := request.AppInstalledOperate{
|
||||
InstallId: appInstall.ID,
|
||||
Operate: constant.Restart,
|
||||
@ -1058,6 +1102,7 @@ func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err
|
||||
_ = fileOp.WriteFile(phpConfigPath, strings.NewReader(string(contentBytes)), 0755)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
4
frontend/components.d.ts
vendored
4
frontend/components.d.ts
vendored
@ -44,7 +44,7 @@ declare module 'vue' {
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
ElImage: typeof import('element-plus/es')['ElImage'];
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElInputNumber: typeof import('element-plus/es')['ElInputNumber'];
|
||||
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
|
||||
ElLink: typeof import('element-plus/es')['ElLink']
|
||||
ElMain: typeof import('element-plus/es')['ElMain'];
|
||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||
@ -76,7 +76,7 @@ declare module 'vue' {
|
||||
FileRole: typeof import('./src/components/file-role/index.vue')['default']
|
||||
FormButton: typeof import('./src/components/layout-content/form-button.vue')['default']
|
||||
Group: typeof import('./src/components/group/index.vue')['default']
|
||||
InfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll'];
|
||||
InfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']
|
||||
LayoutContent: typeof import('./src/components/layout-content/index.vue')['default']
|
||||
Line: typeof import('./src/components/v-charts/components/Line.vue')['default']
|
||||
Loading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||
|
@ -269,11 +269,14 @@ export namespace Website {
|
||||
|
||||
export interface PHPConfig {
|
||||
params: any;
|
||||
disableFunctions: string[];
|
||||
}
|
||||
|
||||
export interface PHPConfigUpdate {
|
||||
id: number;
|
||||
params: any;
|
||||
params?: any;
|
||||
disableFunctions?: string[];
|
||||
scope: string;
|
||||
}
|
||||
|
||||
export interface PHPUpdate {
|
||||
|
@ -277,6 +277,19 @@ const checkConatinerName = (rule: any, value: any, callback: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
const checkDisableFunctions = (rule: any, value: any, callback: any) => {
|
||||
if (value === '' || typeof value === 'undefined' || value == null) {
|
||||
callback(new Error(i18n.global.t('commons.rule.disableFunction')));
|
||||
} else {
|
||||
const reg = /^[a-zA-Z,]+$/;
|
||||
if (!reg.test(value) && value !== '') {
|
||||
callback(new Error(i18n.global.t('commons.rule.disableFunction')));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
interface CommonRule {
|
||||
requiredInput: FormItemRule;
|
||||
requiredSelect: FormItemRule;
|
||||
@ -300,6 +313,7 @@ interface CommonRule {
|
||||
nginxDoc: FormItemRule;
|
||||
appName: FormItemRule;
|
||||
containerName: FormItemRule;
|
||||
disabledFunctions: FormItemRule;
|
||||
|
||||
paramCommon: FormItemRule;
|
||||
paramComplexity: FormItemRule;
|
||||
@ -446,4 +460,9 @@ export const Rules: CommonRule = {
|
||||
trigger: 'blur',
|
||||
validator: checkConatinerName,
|
||||
},
|
||||
disabledFunctions: {
|
||||
required: true,
|
||||
trigger: 'blur',
|
||||
validator: checkDisableFunctions,
|
||||
},
|
||||
};
|
||||
|
@ -159,6 +159,7 @@ const message = {
|
||||
nginxDoc: 'Only supports English case, numbers, and .',
|
||||
appName: 'Support English, numbers, - and _, length 2-30, and cannot start and end with -_',
|
||||
conatinerName: 'Supports letters, numbers, underscores, hyphens and dots, cannot end with hyphen- or dot.',
|
||||
disableFunction: 'Only support letters and,',
|
||||
},
|
||||
res: {
|
||||
paramError: 'The request failed, please try again later!',
|
||||
@ -1384,6 +1385,8 @@ const message = {
|
||||
cgi_fix_pathinfo: 'Whether to open pathinfo',
|
||||
date_timezone: 'Time zone',
|
||||
second: 'Second',
|
||||
disableFunction: 'Disable function',
|
||||
disableFunctionHelper: 'Enter the function to be disabled, such as exec, please use multiple, split',
|
||||
},
|
||||
nginx: {
|
||||
serverNamesHashBucketSizeHelper: 'The hash table size of the server name',
|
||||
|
@ -162,6 +162,7 @@ const message = {
|
||||
nginxDoc: '仅支持英文大小写,数字,和.',
|
||||
appName: '支持英文、数字、-和_,长度2-30,并且不能以-_开头和结尾',
|
||||
conatinerName: '支持字母、数字、下划线、连字符和点,不能以连字符-或点.结尾',
|
||||
disableFunction: '仅支持字母和,',
|
||||
},
|
||||
res: {
|
||||
paramError: '请求失败,请稍后重试!',
|
||||
@ -1365,6 +1366,8 @@ const message = {
|
||||
cgi_fix_pathinfo: '是否开启pathinfo',
|
||||
date_timezone: '时区',
|
||||
second: '秒',
|
||||
disableFunction: '禁用函数',
|
||||
disableFunctionHelper: '输入要禁用的函数,例如exec,多个请用,分割',
|
||||
},
|
||||
nginx: {
|
||||
serverNamesHashBucketSizeHelper: '服务器名字的hash表大小',
|
||||
|
@ -184,7 +184,7 @@ const submit = async () => {
|
||||
display_errors: form.display_errors,
|
||||
};
|
||||
loading.value = true;
|
||||
UpdatePHPConfig({ id: id.value, params: params })
|
||||
UpdatePHPConfig({ id: id.value, params: params, scope: 'params' })
|
||||
.then(() => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||
})
|
||||
|
142
frontend/src/views/website/website/config/php/function/index.vue
Normal file
142
frontend/src/views/website/website/config/php/function/index.vue
Normal file
@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row>
|
||||
<el-col :xs="20" :sm="12" :md="10" :lg="10" :xl="8" :offset="1">
|
||||
<el-form :model="form" :rules="rules" ref="formRef">
|
||||
<el-form-item prop="funcs">
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.funcs"
|
||||
label="value"
|
||||
:placeholder="$t('php.disableFunctionHelper')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<ComplexTable :data="data" v-loading="loading">
|
||||
<template #toolbar>
|
||||
<el-button type="primary" icon="Plus" @click="openCreate(formRef)">
|
||||
{{ $t('commons.button.add') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<el-table-column :label="$t('commons.table.name')" prop="func"></el-table-column>
|
||||
<el-table-column :label="$t('commons.table.operate')">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="primary" @click="remove($index)">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ComplexTable>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<ConfirmDialog ref="confirmDialogRef" @confirm="submit(false, [''])"></ConfirmDialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { GetPHPConfig, UpdatePHPConfig } from '@/api/modules/website';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { onMounted, reactive } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
funcs: [Rules.requiredInput, Rules.disabledFunctions],
|
||||
});
|
||||
|
||||
const websiteID = computed(() => {
|
||||
return props.id;
|
||||
});
|
||||
const formRef = ref();
|
||||
const loading = ref(false);
|
||||
const form = ref({
|
||||
funcs: '',
|
||||
});
|
||||
const data = ref([]);
|
||||
const confirmDialogRef = ref();
|
||||
|
||||
const search = () => {
|
||||
loading.value = true;
|
||||
GetPHPConfig(websiteID.value)
|
||||
.then((res) => {
|
||||
const functions = res.data.disableFunctions || [];
|
||||
if (functions.length > 0) {
|
||||
functions.forEach((value: string) => {
|
||||
data.value.push({
|
||||
func: value,
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const openCreate = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
let params = {
|
||||
header: i18n.global.t('database.confChange'),
|
||||
operationInfo: i18n.global.t('database.restartNowHelper'),
|
||||
submitInputInfo: i18n.global.t('database.restartNow'),
|
||||
};
|
||||
confirmDialogRef.value!.acceptParams(params);
|
||||
});
|
||||
};
|
||||
|
||||
const remove = async (index: number) => {
|
||||
ElMessageBox.confirm(i18n.global.t('commons.msg.delete'), i18n.global.t('commons.msg.deleteTitle'), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
type: 'info',
|
||||
})
|
||||
.then(() => {
|
||||
const copyList = data.value.concat();
|
||||
copyList.splice(index, 1);
|
||||
const funcArray: string[] = [];
|
||||
copyList.forEach((d) => {
|
||||
funcArray.push(d.func);
|
||||
});
|
||||
submit(true, funcArray);
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const submit = async (del: boolean, funcArray: string[]) => {
|
||||
let disableFunctions = [];
|
||||
if (del) {
|
||||
disableFunctions = funcArray;
|
||||
} else {
|
||||
disableFunctions = form.value.funcs.split(',');
|
||||
data.value.forEach((d) => {
|
||||
disableFunctions.push(d.func);
|
||||
});
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
UpdatePHPConfig({ scope: 'disable_functions', id: websiteID.value, disableFunctions: disableFunctions })
|
||||
.then(() => {
|
||||
form.value.funcs = '';
|
||||
data.value = [];
|
||||
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||
search();
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
</script>
|
@ -3,6 +3,9 @@
|
||||
<el-tab-pane :label="$t('website.updateConfig')" name="0">
|
||||
<Config :id="id"></Config>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('php.disableFunction')" name="1">
|
||||
<Function :id="id"></Function>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
@ -11,6 +14,7 @@ import { GetRuntime } from '@/api/modules/runtime';
|
||||
import { GetWebsite } from '@/api/modules/website';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import Config from './config/index.vue';
|
||||
import Function from './function/index.vue';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
@ -23,9 +27,9 @@ const id = computed(() => {
|
||||
return props.id;
|
||||
});
|
||||
|
||||
let index = ref('0');
|
||||
let configPHP = ref(false);
|
||||
let installId = ref(0);
|
||||
const index = ref('0');
|
||||
const configPHP = ref(false);
|
||||
const installId = ref(0);
|
||||
|
||||
const getWebsiteDetail = async () => {
|
||||
const res = await GetWebsite(props.id);
|
||||
|
@ -153,7 +153,6 @@
|
||||
</el-card>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
<NginxConfig v-if="openNginxConfig" v-loading="loading" :containerName="containerName" :status="nginxStatus" />
|
||||
<CreateWebSite ref="createRef" @close="search" />
|
||||
<DeleteWebsite ref="deleteRef" @close="search" />
|
||||
<UploadDialog ref="uploadRef" />
|
||||
@ -161,6 +160,7 @@
|
||||
<DefaultServer ref="defaultRef" />
|
||||
<GroupDialog @search="listGroup" ref="groupRef" />
|
||||
</div>
|
||||
<NginxConfig v-if="openNginxConfig" v-loading="loading" :containerName="containerName" :status="nginxStatus" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
Loading…
Reference in New Issue
Block a user