refactor(access): fold allowed_users into allowed_principals
Move the model-route access modal off the deprecated allowed_users policy/field onto the unified allowed_principals + principals surface, persisting everything through a single /access POST. - "specific users" radio now uses allowed_principals; a returned legacy allowed_users value is normalized so existing routes still select it. - derive the picker's selection and the full grant set from `principals` in GET /access (fall back to legacy `items` if a backend doesn't return principals yet). - save as `principals`: the principal-based override sends its staged set; the user picker maps its selection to USER-kind grants and preserves any non-user grants from the snapshot (no longer sends `users`). - guard saving before the GET seeds principals (would wipe grants); share the ALLOWED_PRINCIPALS_POLICY constant. - AccessControlFormData: `users` optional, add `principals`.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { RouteItem } from '@/pages/model-routes/config/types';
|
||||
import { getGPUStackPlugin } from '@/plugins';
|
||||
import { AlertBlockInfo, FormDrawer, ModalFooter } from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { message } from 'antd';
|
||||
@@ -7,7 +8,7 @@ import _ from 'lodash';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { updateModelAccessUser } from '../../apis';
|
||||
import { AccessControlFormData } from '../../config/types';
|
||||
import AccessControlForm from './form';
|
||||
import AccessControlForm, { ALLOWED_PRINCIPALS_POLICY } from './form';
|
||||
|
||||
const AccessControlModal: React.FC<
|
||||
Global.ScrollerModalProps<RouteItem, AccessControlFormData>
|
||||
@@ -23,17 +24,55 @@ const AccessControlModal: React.FC<
|
||||
|
||||
const handleOnFinish = async (values: AccessControlFormData) => {
|
||||
try {
|
||||
const data: AccessControlFormData = {
|
||||
access_policy: values.access_policy,
|
||||
// `users` is only meaningful for the legacy `allowed_users`
|
||||
// policy; for the plugin override (typically the principal-
|
||||
// based policy) the plugin's Field manages its own state
|
||||
// inline via the principal CRUD endpoints, so we send an
|
||||
// empty list to clear any stale user grants from a prior
|
||||
// policy switch.
|
||||
users:
|
||||
values.access_policy === 'allowed_users' ? values.users || [] : []
|
||||
};
|
||||
// Everything saves through the single `/access` POST as
|
||||
// `principals` (one request, one success toast):
|
||||
// * principal-based override active → its staged full grant set.
|
||||
// * user picker → selected users mapped to USER-kind grants,
|
||||
// plus any non-user grants preserved from the GET snapshot
|
||||
// (so a user-only UI never wipes org/group grants it can't
|
||||
// see — though in practice the picker only runs where none
|
||||
// exist).
|
||||
// * otherwise (authed/public) → send neither, so the server
|
||||
// leaves existing grants untouched.
|
||||
const override = getGPUStackPlugin()?.accessControl?.allowedUsersOverride;
|
||||
const usesPrincipals =
|
||||
!!override && values.access_policy === override.policyValue;
|
||||
const usesUserList =
|
||||
!override && values.access_policy === ALLOWED_PRINCIPALS_POLICY;
|
||||
|
||||
// Guard the load race: on an existing route, `principals` is
|
||||
// undefined until the GET seeds it. Saving in that window would
|
||||
// submit an empty grant set and wipe existing grants — bail out
|
||||
// (the modal stays open; the user can retry once loaded).
|
||||
if (
|
||||
currentData?.id &&
|
||||
(usesPrincipals || usesUserList) &&
|
||||
!values.principals
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data: AccessControlFormData;
|
||||
if (usesPrincipals) {
|
||||
data = {
|
||||
access_policy: values.access_policy,
|
||||
principals: values.principals || []
|
||||
};
|
||||
} else if (usesUserList) {
|
||||
const preserved = (values.principals || []).filter(
|
||||
(p) => p.principal_type !== 'user'
|
||||
);
|
||||
const userGrants = (values.users || []).map((u) => ({
|
||||
principal_type: 'user',
|
||||
principal_id: u.id
|
||||
}));
|
||||
data = {
|
||||
access_policy: values.access_policy,
|
||||
principals: [...preserved, ...userGrants]
|
||||
};
|
||||
} else {
|
||||
data = { access_policy: values.access_policy };
|
||||
}
|
||||
await updateModelAccessUser({
|
||||
id: currentData?.id as number,
|
||||
data: data
|
||||
|
||||
Reference in New Issue
Block a user