fix: tighten rbac and release resources (#12886)

This commit is contained in:
王贺
2026-05-28 11:09:00 +08:00
committed by GitHub
parent adb97730c1
commit 4f78060d35
14 changed files with 42 additions and 28 deletions

View File

@@ -748,7 +748,6 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
if err != nil {
return err
}
dockerCLi, _ := docker.NewClient()
if req.PullImage {
composeContent := []byte(detail.DockerCompose)
if req.DockerCompose != "" {
@@ -758,6 +757,11 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
if err != nil {
return err
}
dockerCLi, err := docker.NewClient()
if err != nil {
return err
}
defer dockerCLi.Close()
for _, image := range images {
t.Log(i18n.GetWithName("PullImageStart", image))
if err = dockerCLi.PullImageWithProcess(t, image); err != nil {
@@ -1182,6 +1186,7 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error
if err != nil {
return err
}
defer dockerCLi.Close()
for _, image := range images {
if imagePrefix != "" {
lastSlashIndex := strings.LastIndex(image, "/")

View File

@@ -377,6 +377,7 @@ func snapAppImage(snap snapHelper, req dto.SnapshotCreate, targetDir string) err
snap.Task.Log("load docker client failed, skip save app images")
return nil
}
defer client.Close()
images, err := client.ImageList(context.Background(), image.ListOptions{})
if err != nil {
snap.Task.Log("list docker images failed, skip save app images")

View File

@@ -503,6 +503,7 @@ func loadImage(dbType, version string) (string, error) {
if err != nil {
return "", err
}
defer cli.Close()
images, err := cli.ImageList(context.Background(), image.ListOptions{})
if err != nil {
return "", err

View File

@@ -40,6 +40,7 @@ func HandleGet(url string) (*http.Response, error) {
}
}
if resp.StatusCode == 404 {
_ = resp.Body.Close()
return nil, buserr.New("ErrHttpReqNotFound")
}
@@ -71,6 +72,7 @@ func HandleRequestWithClient(client *http.Client, url, method string, timeout in
if err != nil {
return 0, nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return 0, nil, errors.New(resp.Status)
}
@@ -78,7 +80,6 @@ func HandleRequestWithClient(client *http.Client, url, method string, timeout in
if err != nil {
return 0, nil, err
}
defer resp.Body.Close()
return resp.StatusCode, body, nil
}
@@ -143,6 +144,7 @@ func RequestFile(url, method string, timeout int) (io.ReadCloser, context.Cancel
return nil, cancel, err
}
if resp.StatusCode != http.StatusOK {
_ = resp.Body.Close()
return nil, cancel, errors.New(resp.Status)
}
return resp.Body, cancel, nil

View File

@@ -190,6 +190,7 @@ func HandleRequest(url, method string, timeout int) (int, []byte, error) {
if err != nil {
return 0, nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return 0, nil, errors.New(resp.Status)
}
@@ -197,7 +198,6 @@ func HandleRequest(url, method string, timeout int) (int, []byte, error) {
if err != nil {
return 0, nil, err
}
defer resp.Body.Close()
return resp.StatusCode, body, nil
}

View File

@@ -807,10 +807,10 @@ func checkProxy(req dto.ProxyUpdate) error {
if err != nil {
return buserr.WithErr("ErrProxySetting", err)
}
defer resp.Body.Close()
if _, err := io.ReadAll(resp.Body); err != nil {
return buserr.WithErr("ErrProxySetting", err)
}
defer resp.Body.Close()
return nil
}

View File

@@ -314,11 +314,11 @@ func (u *UpgradeService) LoadRelease() ([]dto.ReleasesNotes, error) {
if err != nil {
return notes, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return notes, err
}
defer resp.Body.Close()
var nodeItem noteHelper
if err := json.Unmarshal(body, &nodeItem); err != nil {
return notes, err

View File

@@ -54,11 +54,11 @@ func handleRequestWithTransport(url, method string, transport *http.Transport, t
if err != nil {
return 0, nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return 0, nil, err
}
defer resp.Body.Close()
return resp.StatusCode, body, nil
}
@@ -106,6 +106,7 @@ func handleGetWithTransport(url string, transport *http.Transport) (*http.Respon
}
}
if resp.StatusCode == 404 {
_ = resp.Body.Close()
return nil, buserr.WithErr("ErrHttpReqNotFound", errors.New("no such resource"))
}

View File

@@ -55,6 +55,7 @@ export namespace Login {
mfaInterval: number;
role: string;
permissions: string[];
masterOnlyPermissions?: string[];
nodeRoles: Array<{ nodeId: number; nodeName: string; roleId: number; roleName: string }>;
apiInterfaceStatus: string;

View File

@@ -57,6 +57,7 @@ export interface GlobalState {
// tags
isAdmin: boolean;
permissions: string[];
masterOnlyPermissions: string[];
nodeRoles: Array<{ nodeId: number; nodeName: string; roleId: number; roleName: string }>;
isEnterprise: boolean;
isIntl: boolean;

View File

@@ -4,7 +4,7 @@ import piniaPersistConfig from '@/config/pinia-persist';
import { GlobalState } from '../interface';
import { DeviceType } from '@/enums/app';
import i18n, { setActiveLocale } from '@/lang';
import { isMasterOnlyPermissionCode, toManageCode } from '@/utils/permission-codes';
import { isMasterOnlyPermissionCode, setMasterOnlyPermissionCodes, toManageCode } from '@/utils/permission-codes';
const CN_DOCS_URL = 'https://1panel.cn/docs/v2';
const INTL_DOCS_URL = 'https://docs.1panel.pro/v2';
@@ -55,6 +55,7 @@ const GlobalStore = defineStore('GlobalState', {
// tags
isAdmin: false,
permissions: [],
masterOnlyPermissions: [],
nodeRoles: [],
isEnterprise: false,
isIntl: false,
@@ -109,18 +110,24 @@ const GlobalStore = defineStore('GlobalState', {
setAuthInfo(payload: {
isAdmin: boolean;
permissions: string[];
masterOnlyPermissions?: string[];
nodeRoles?: Array<{ nodeId: number; nodeName: string; roleId: number; roleName: string }>;
}) {
this.isAdmin = !!payload.isAdmin;
this.permissions = payload.permissions || [];
this.masterOnlyPermissions = payload.masterOnlyPermissions || [];
this.nodeRoles = payload.nodeRoles || [];
setMasterOnlyPermissionCodes(this.masterOnlyPermissions);
},
clearAuthInfo() {
this.permissions = [];
this.masterOnlyPermissions = [];
this.nodeRoles = [];
this.isAdmin = false;
setMasterOnlyPermissionCodes([]);
},
hasPermission(permission: string) {
setMasterOnlyPermissionCodes(this.masterOnlyPermissions);
const normalizedPermission = permission.trim();
if (!normalizedPermission) {
return false;

View File

@@ -1,14 +1,15 @@
const masterOnlyPermissionCodes = new Set([
'ai_proxy_view',
'ai_proxy_manage',
'ai_proxy_key_view',
'ai_benchmark_view',
'ai_benchmark_manage',
'ai_skills_hub_view',
'ai_skills_hub_manage',
'xpack_ops_report_view',
'xpack_ops_report_manage',
]);
let masterOnlyPermissionCodes = new Set<string>();
let masterOnlyPermissionCodesKey = '';
export const setMasterOnlyPermissionCodes = (permissions?: string[]) => {
const codes = (permissions || []).filter(Boolean);
const key = codes.join('\n');
if (key === masterOnlyPermissionCodesKey) {
return;
}
masterOnlyPermissionCodesKey = key;
masterOnlyPermissionCodes = new Set(codes);
};
export const toManageCode = (permission: string): string => {
if (!permission) {

View File

@@ -1,6 +1,6 @@
import router from '@/routers';
import { GlobalStore } from '@/store';
import { isMasterOnlyPermissionCode, normalizeToManageCode } from '@/utils/permission-codes';
import { normalizeToManageCode } from '@/utils/permission-codes';
export type PermissionBindingValue = string | string[] | undefined;
export type PermissionMode = 'manage' | 'view';
@@ -51,15 +51,7 @@ const hasPermissionAccessByMode = (mode: PermissionMode, value?: PermissionBindi
const permissions = toPermissionList(value);
const normalizedPermissions = mode === 'manage' ? permissions.map(toManagePermission).filter(Boolean) : permissions;
if (normalizedPermissions.length === 0) {
return globalStore.isAdmin || globalStore.isNodeAdmin;
}
if (
mode === 'manage' &&
globalStore.isNodeAdmin &&
!globalStore.isMaster &&
normalizedPermissions.some((permission) => !isMasterOnlyPermissionCode(permission))
) {
return true;
return globalStore.isAdmin;
}
return normalizedPermissions.some((permission) => globalStore.hasPermission(permission));
};

View File

@@ -25,6 +25,7 @@ export const syncAuthInfo = async (currentNode?: string) => {
globalStore.setAuthInfo({
isAdmin: res.data.role === 'ADMIN',
permissions: res.data.permissions || [],
masterOnlyPermissions: res.data.masterOnlyPermissions || [],
nodeRoles: res.data.nodeRoles || [],
});
return res.data;
@@ -33,6 +34,7 @@ export const syncAuthInfo = async (currentNode?: string) => {
globalStore.setAuthInfo({
isAdmin: res.data.role === 'ADMIN',
permissions: res.data.permissions || [],
masterOnlyPermissions: res.data.masterOnlyPermissions || [],
nodeRoles: res.data.nodeRoles || [],
});
return res.data;