fix: source owner_principal_id from current organization
OwnerPrincipalIdField was reading currentClusterAtom, which no callers ever wrote — so every form submission ended up with owner_principal_id=null and the backend rejected non-admins with "Only platform admin can create global ...". Pin the field to currentOrganizationId instead (the Org the caller is acting under). Cluster ownership is the wrong source: a cluster_access grant lets one Org schedule on another Org's cluster, but the new resource is still owned by the caller's Org, and the backend enforces owner_principal_id == ctx.current_principal_id. Also drops the dead currentClusterAtom.
This commit is contained in:
@@ -1,6 +0,0 @@
|
|||||||
import { ClusterListItem } from '@/pages/cluster-management/config/types';
|
|
||||||
import { atom } from 'jotai';
|
|
||||||
|
|
||||||
export const currentClusterAtom = atom<
|
|
||||||
(Partial<ClusterListItem> & { label?: string; value?: number }) | null
|
|
||||||
>(null);
|
|
||||||
@@ -3,6 +3,15 @@ import { atomWithStorage } from 'jotai/utils';
|
|||||||
|
|
||||||
export const userAtom = atomWithStorage<any>('userInfo', null);
|
export const userAtom = atomWithStorage<any>('userInfo', null);
|
||||||
|
|
||||||
|
// Backs the `currentOrganizationId` localStorage key. Stays null in
|
||||||
|
// builds with no Org context (single-tenant), and is shared with any
|
||||||
|
// extension that persists the same key so both sides stay in sync
|
||||||
|
// without one side having to import from the other.
|
||||||
|
export const currentOrganizationIdAtom = atomWithStorage<number | null>(
|
||||||
|
'currentOrganizationId',
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
export const GPUStackVersionAtom = atom<{
|
export const GPUStackVersionAtom = atom<{
|
||||||
version: string;
|
version: string;
|
||||||
git_commit: string;
|
git_commit: string;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { currentClusterAtom } from '@/atoms/gpuservice';
|
import { currentOrganizationIdAtom } from '@/atoms/user';
|
||||||
import { Input as CInput } from '@gpustack/core-ui';
|
import { Input as CInput } from '@gpustack/core-ui';
|
||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
import type { NamePath } from 'antd/es/form/interface';
|
import type { NamePath } from 'antd/es/form/interface';
|
||||||
@@ -9,18 +9,22 @@ interface OwnerPrincipalIdFieldProps {
|
|||||||
name?: NamePath;
|
name?: NamePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pins `owner_principal_id` to the Org the caller is currently acting
|
||||||
|
// under. Cluster ownership is irrelevant here: cluster_access grants
|
||||||
|
// let one Org schedule on another Org's cluster, but the resource the
|
||||||
|
// caller creates still belongs to *their* Org, and the backend enforces
|
||||||
|
// `owner_principal_id == ctx.current_principal_id`.
|
||||||
const OwnerPrincipalIdField: React.FC<OwnerPrincipalIdFieldProps> = ({
|
const OwnerPrincipalIdField: React.FC<OwnerPrincipalIdFieldProps> = ({
|
||||||
name = 'owner_principal_id'
|
name = 'owner_principal_id'
|
||||||
}) => {
|
}) => {
|
||||||
const currentCluster = useAtomValue(currentClusterAtom);
|
const currentOrgId = useAtomValue(currentOrganizationIdAtom);
|
||||||
const ownerPrincipalId = currentCluster?.owner_principal_id;
|
|
||||||
const form = Form.useFormInstance();
|
const form = Form.useFormInstance();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ownerPrincipalId != null) {
|
if (currentOrgId != null) {
|
||||||
form.setFieldValue(name, ownerPrincipalId);
|
form.setFieldValue(name, currentOrgId);
|
||||||
}
|
}
|
||||||
}, [ownerPrincipalId, name, form]);
|
}, [currentOrgId, name, form]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form.Item name={name} hidden>
|
<Form.Item name={name} hidden>
|
||||||
|
|||||||
Reference in New Issue
Block a user