✨ feat: Add Group Access Control to Model Specifications and Update User Filtering Logic
This commit is contained in:
@@ -19,9 +19,22 @@ export default function ModelSpecsMenu({ modelSpecs }: { modelSpecs?: TModelSpec
|
||||
const localize = useLocalize();
|
||||
const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery();
|
||||
const modularChat = useRecoilValue(store.modularChat);
|
||||
const user = useRecoilValue(store.user);
|
||||
const getDefaultConversation = useDefaultConvo();
|
||||
const assistantMap = useAssistantsMapContext();
|
||||
|
||||
const allowedModelSpecs = useMemo(() => {
|
||||
if (!modelSpecs) {return [];}
|
||||
return modelSpecs.filter(spec => {
|
||||
// If no groups defined for spec, allow it.
|
||||
if (!spec.groups || spec.groups.length === 0) {return true;}
|
||||
// Otherwise, check if the user exists and has groups.
|
||||
if (!user || !user.groups || user.groups.length === 0) {return false;}
|
||||
// Check if at least one of the spec's groups is in the user's groups.
|
||||
return spec.groups.some(groupId => user.groups.includes(groupId));
|
||||
});
|
||||
}, [modelSpecs, user]);
|
||||
|
||||
const onSelectSpec = (spec: TModelSpec) => {
|
||||
const { preset } = spec;
|
||||
preset.iconURL = getModelSpecIconURL(spec);
|
||||
@@ -82,21 +95,15 @@ export default function ModelSpecsMenu({ modelSpecs }: { modelSpecs?: TModelSpec
|
||||
};
|
||||
|
||||
const selected = useMemo(() => {
|
||||
const spec = modelSpecs?.find((spec) => spec.name === conversation?.spec);
|
||||
if (!spec) {
|
||||
return undefined;
|
||||
}
|
||||
return spec;
|
||||
}, [modelSpecs, conversation?.spec]);
|
||||
const spec = allowedModelSpecs.find((spec) => spec.name === conversation?.spec);
|
||||
return spec || undefined;
|
||||
}, [allowedModelSpecs, conversation?.spec]);
|
||||
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleKeyDown = useCallback((event: KeyboardEvent) => {
|
||||
const menuItems = menuRef.current?.querySelectorAll('[role="option"]');
|
||||
if (!menuItems) {
|
||||
return;
|
||||
}
|
||||
if (!menuItems.length) {
|
||||
if (!menuItems || !menuItems.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -132,7 +139,7 @@ export default function ModelSpecsMenu({ modelSpecs }: { modelSpecs?: TModelSpec
|
||||
endpointsConfig={endpointsConfig}
|
||||
/>
|
||||
<Portal>
|
||||
{modelSpecs && modelSpecs.length && (
|
||||
{allowedModelSpecs && allowedModelSpecs.length > 0 && (
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
@@ -154,7 +161,7 @@ export default function ModelSpecsMenu({ modelSpecs }: { modelSpecs?: TModelSpec
|
||||
className="models-scrollbar mt-2 max-h-[65vh] min-w-[340px] max-w-xs overflow-y-auto rounded-lg border border-gray-100 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-700 dark:text-white lg:max-h-[75vh]"
|
||||
>
|
||||
<ModelSpecs
|
||||
specs={modelSpecs}
|
||||
specs={allowedModelSpecs}
|
||||
selected={selected}
|
||||
setSelected={onSelectSpec}
|
||||
endpointsConfig={endpointsConfig}
|
||||
|
||||
@@ -19,6 +19,9 @@ export type TModelSpec = {
|
||||
showIconInHeader?: boolean;
|
||||
iconURL?: string | EModelEndpoint; // Allow using project-included icons
|
||||
authType?: AuthType;
|
||||
groups?: Array<string>; // List of group ObjectIds allowed to access this model
|
||||
// badgeIcon?: string; // URL to badge icon for visual categorization
|
||||
// badgeTooltip?: string; // Tooltip text for the badge
|
||||
};
|
||||
|
||||
export const tModelSpecSchema = z.object({
|
||||
@@ -32,6 +35,9 @@ export const tModelSpecSchema = z.object({
|
||||
showIconInHeader: z.boolean().optional(),
|
||||
iconURL: z.union([z.string(), eModelEndpointSchema]).optional(),
|
||||
authType: authTypeSchema.optional(),
|
||||
groups: z.array(z.string()).optional(),
|
||||
// badgeIcon: z.string().url('Must be a valid URL').optional(),
|
||||
// badgeTooltip: z.string().optional(),
|
||||
});
|
||||
|
||||
export const specsConfigSchema = z.object({
|
||||
|
||||
@@ -106,6 +106,16 @@ export type TBackupCode = {
|
||||
usedAt: Date | null;
|
||||
};
|
||||
|
||||
export type TGroup = {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
externalId?: string;
|
||||
provider: 'local' | 'openid';
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
};
|
||||
|
||||
export type TUser = {
|
||||
id: string;
|
||||
username: string;
|
||||
@@ -116,6 +126,7 @@ export type TUser = {
|
||||
provider: string;
|
||||
plugins?: string[];
|
||||
backupCodes?: TBackupCode[];
|
||||
groups: string[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user