mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-27 05:38:51 +01:00
🔐 feat: Granular Role-based Permissions + Entra ID Group Discovery (#7804)
WIP: pre-granular-permissions commit
feat: Add category and support contact fields to Agent schema and UI components
Revert "feat: Add category and support contact fields to Agent schema and UI components"
This reverts commit c43a52b4c9.
Fix: Update import for renderHook in useAgentCategories.spec.tsx
fix: Update icon rendering in AgentCategoryDisplay tests to use empty spans
refactor: Improve category synchronization logic and clean up AgentConfig component
refactor: Remove unused UI flow translations from translation.json
feat: agent marketplace features
🔐 feat: Granular Role-based Permissions + Entra ID Group Discovery (#7804)
This commit is contained in:
parent
abc32e66ce
commit
76d75030b9
147 changed files with 17564 additions and 645 deletions
292
packages/data-provider/src/accessPermissions.ts
Normal file
292
packages/data-provider/src/accessPermissions.ts
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* Granular Permission System Types for Agent Sharing
|
||||
*
|
||||
* This file contains TypeScript interfaces and Zod schemas for the enhanced
|
||||
* agent permission system that supports sharing with specific users/groups
|
||||
* and Entra ID integration.
|
||||
*/
|
||||
|
||||
// ===== ENUMS & CONSTANTS =====
|
||||
|
||||
/**
|
||||
* Principal types for permission system
|
||||
*/
|
||||
export type TPrincipalType = 'user' | 'group' | 'public';
|
||||
|
||||
/**
|
||||
* Source of the principal (local LibreChat or external Entra ID)
|
||||
*/
|
||||
export type TPrincipalSource = 'local' | 'entra';
|
||||
|
||||
/**
|
||||
* Access levels for agents
|
||||
*/
|
||||
export type TAccessLevel = 'none' | 'viewer' | 'editor' | 'owner';
|
||||
|
||||
/**
|
||||
* Permission bit constants for bitwise operations
|
||||
*/
|
||||
export const PERMISSION_BITS = {
|
||||
VIEW: 1, // 001 - Can view and use agent
|
||||
EDIT: 2, // 010 - Can modify agent settings
|
||||
DELETE: 4, // 100 - Can delete agent
|
||||
SHARE: 8, // 1000 - Can share agent with others (future)
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Standard access role IDs
|
||||
*/
|
||||
export const ACCESS_ROLE_IDS = {
|
||||
AGENT_VIEWER: 'agent_viewer',
|
||||
AGENT_EDITOR: 'agent_editor',
|
||||
AGENT_OWNER: 'agent_owner', // Future use
|
||||
} as const;
|
||||
|
||||
// ===== ZOD SCHEMAS =====
|
||||
|
||||
/**
|
||||
* Principal schema - represents a user, group, or public access
|
||||
*/
|
||||
export const principalSchema = z.object({
|
||||
type: z.enum(['user', 'group', 'public']),
|
||||
id: z.string().optional(), // undefined for 'public' type
|
||||
name: z.string().optional(),
|
||||
email: z.string().optional(), // for user and group types
|
||||
source: z.enum(['local', 'entra']).optional(),
|
||||
avatar: z.string().optional(), // for user and group types
|
||||
description: z.string().optional(), // for group type
|
||||
idOnTheSource: z.string().optional(), // Entra ID for users/groups
|
||||
accessRoleId: z.string().optional(), // Access role ID for permissions
|
||||
memberCount: z.number().optional(), // for group type
|
||||
});
|
||||
|
||||
/**
|
||||
* Access role schema - defines named permission sets
|
||||
*/
|
||||
export const accessRoleSchema = z.object({
|
||||
accessRoleId: z.string(),
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
resourceType: z.string().default('agent'),
|
||||
permBits: z.number(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Permission entry schema - represents a single ACL entry
|
||||
*/
|
||||
export const permissionEntrySchema = z.object({
|
||||
id: z.string(),
|
||||
principalType: z.enum(['user', 'group', 'public']),
|
||||
principalId: z.string().optional(), // undefined for 'public'
|
||||
principalName: z.string().optional(),
|
||||
role: accessRoleSchema,
|
||||
grantedBy: z.string(),
|
||||
grantedAt: z.string(), // ISO date string
|
||||
inheritedFrom: z.string().optional(), // for project-level inheritance
|
||||
source: z.enum(['local', 'entra']).optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Resource permissions response schema
|
||||
*/
|
||||
export const resourcePermissionsResponseSchema = z.object({
|
||||
resourceType: z.string(),
|
||||
resourceId: z.string(),
|
||||
permissions: z.array(permissionEntrySchema),
|
||||
});
|
||||
|
||||
/**
|
||||
* Update resource permissions request schema
|
||||
* This matches the user's requirement for the frontend DTO structure
|
||||
*/
|
||||
export const updateResourcePermissionsRequestSchema = z.object({
|
||||
updated: principalSchema.array(),
|
||||
removed: principalSchema.array(),
|
||||
public: z.boolean(),
|
||||
publicAccessRoleId: z.string().optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Update resource permissions response schema
|
||||
* Returns the updated permissions with accessRoleId included
|
||||
*/
|
||||
export const updateResourcePermissionsResponseSchema = z.object({
|
||||
message: z.string(),
|
||||
results: z.object({
|
||||
principals: principalSchema.array(),
|
||||
public: z.boolean(),
|
||||
publicAccessRoleId: z.string().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
// ===== TYPESCRIPT TYPES =====
|
||||
|
||||
/**
|
||||
* Principal - represents a user, group, or public access
|
||||
*/
|
||||
export type TPrincipal = z.infer<typeof principalSchema>;
|
||||
|
||||
/**
|
||||
* Access role - defines named permission sets
|
||||
*/
|
||||
export type TAccessRole = z.infer<typeof accessRoleSchema>;
|
||||
|
||||
/**
|
||||
* Permission entry - represents a single ACL entry
|
||||
*/
|
||||
export type TPermissionEntry = z.infer<typeof permissionEntrySchema>;
|
||||
|
||||
/**
|
||||
* Resource permissions response
|
||||
*/
|
||||
export type TResourcePermissionsResponse = z.infer<typeof resourcePermissionsResponseSchema>;
|
||||
|
||||
/**
|
||||
* Update resource permissions request
|
||||
* This matches the user's requirement for the frontend DTO structure
|
||||
*/
|
||||
export type TUpdateResourcePermissionsRequest = z.infer<
|
||||
typeof updateResourcePermissionsRequestSchema
|
||||
>;
|
||||
|
||||
/**
|
||||
* Update resource permissions response
|
||||
* Returns the updated permissions with accessRoleId included
|
||||
*/
|
||||
export type TUpdateResourcePermissionsResponse = z.infer<
|
||||
typeof updateResourcePermissionsResponseSchema
|
||||
>;
|
||||
|
||||
/**
|
||||
* Principal search request parameters
|
||||
*/
|
||||
export type TPrincipalSearchParams = {
|
||||
q: string; // search query (required)
|
||||
limit?: number; // max results (1-50, default 10)
|
||||
type?: 'user' | 'group'; // filter by type (optional)
|
||||
};
|
||||
|
||||
/**
|
||||
* Principal search result item
|
||||
*/
|
||||
export type TPrincipalSearchResult = {
|
||||
id?: string | null; // null for Entra ID principals that don't exist locally yet
|
||||
type: 'user' | 'group';
|
||||
name: string;
|
||||
email?: string; // for users and groups
|
||||
username?: string; // for users
|
||||
avatar?: string; // for users and groups
|
||||
provider?: string; // for users
|
||||
source: 'local' | 'entra';
|
||||
memberCount?: number; // for groups
|
||||
description?: string; // for groups
|
||||
idOnTheSource?: string; // Entra ID for users (maps to openidId) and groups (maps to idOnTheSource)
|
||||
};
|
||||
|
||||
/**
|
||||
* Principal search response
|
||||
*/
|
||||
export type TPrincipalSearchResponse = {
|
||||
query: string;
|
||||
limit: number;
|
||||
type?: 'user' | 'group';
|
||||
results: TPrincipalSearchResult[];
|
||||
count: number;
|
||||
sources: {
|
||||
local: number;
|
||||
entra: number;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Available roles response
|
||||
*/
|
||||
export type TAvailableRolesResponse = {
|
||||
resourceType: string;
|
||||
roles: TAccessRole[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get resource permissions response schema
|
||||
* This matches the enhanced aggregation-based endpoint response format
|
||||
*/
|
||||
export const getResourcePermissionsResponseSchema = z.object({
|
||||
resourceType: z.string(),
|
||||
resourceId: z.string(),
|
||||
principals: z.array(principalSchema),
|
||||
public: z.boolean(),
|
||||
publicAccessRoleId: z.string().optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Get resource permissions response type
|
||||
* This matches the enhanced aggregation-based endpoint response format
|
||||
*/
|
||||
export type TGetResourcePermissionsResponse = z.infer<typeof getResourcePermissionsResponseSchema>;
|
||||
|
||||
/**
|
||||
* Effective permissions response schema
|
||||
* Returns just the permission bitmask for a user on a resource
|
||||
*/
|
||||
export const effectivePermissionsResponseSchema = z.object({
|
||||
permissionBits: z.number(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Effective permissions response type
|
||||
* Returns just the permission bitmask for a user on a resource
|
||||
*/
|
||||
export type TEffectivePermissionsResponse = z.infer<typeof effectivePermissionsResponseSchema>;
|
||||
|
||||
// ===== UTILITY TYPES =====
|
||||
|
||||
/**
|
||||
* Permission check result
|
||||
*/
|
||||
export interface TPermissionCheck {
|
||||
canView: boolean;
|
||||
canEdit: boolean;
|
||||
canDelete: boolean;
|
||||
canShare: boolean;
|
||||
accessLevel: TAccessLevel;
|
||||
}
|
||||
|
||||
// ===== HELPER FUNCTIONS =====
|
||||
|
||||
/**
|
||||
* Convert permission bits to access level
|
||||
*/
|
||||
export function permBitsToAccessLevel(permBits: number): TAccessLevel {
|
||||
if ((permBits & PERMISSION_BITS.DELETE) > 0) return 'owner';
|
||||
if ((permBits & PERMISSION_BITS.EDIT) > 0) return 'editor';
|
||||
if ((permBits & PERMISSION_BITS.VIEW) > 0) return 'viewer';
|
||||
return 'none';
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert access role ID to permission bits
|
||||
*/
|
||||
export function accessRoleToPermBits(accessRoleId: string): number {
|
||||
switch (accessRoleId) {
|
||||
case ACCESS_ROLE_IDS.AGENT_VIEWER:
|
||||
return PERMISSION_BITS.VIEW;
|
||||
case ACCESS_ROLE_IDS.AGENT_EDITOR:
|
||||
return PERMISSION_BITS.VIEW | PERMISSION_BITS.EDIT;
|
||||
case ACCESS_ROLE_IDS.AGENT_OWNER:
|
||||
return PERMISSION_BITS.VIEW | PERMISSION_BITS.EDIT | PERMISSION_BITS.DELETE;
|
||||
default:
|
||||
return PERMISSION_BITS.VIEW;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if permission bitmask contains other bitmask
|
||||
* @param permissions - The permission bitmask to check
|
||||
* @param requiredPermission - The required permission bit(s)
|
||||
* @returns {boolean} Whether permissions contains requiredPermission
|
||||
*/
|
||||
export function hasPermissions(permissions: number, requiredPermission: number): boolean {
|
||||
return (permissions & requiredPermission) === requiredPermission;
|
||||
}
|
||||
|
|
@ -306,6 +306,32 @@ export const memories = () => '/api/memories';
|
|||
export const memory = (key: string) => `${memories()}/${encodeURIComponent(key)}`;
|
||||
export const memoryPreferences = () => `${memories()}/preferences`;
|
||||
|
||||
export const searchPrincipals = (params: q.PrincipalSearchParams) => {
|
||||
const { q: query, limit, type } = params;
|
||||
let url = `/api/permissions/search-principals?q=${encodeURIComponent(query)}`;
|
||||
|
||||
if (limit !== undefined) {
|
||||
url += `&limit=${limit}`;
|
||||
}
|
||||
|
||||
if (type !== undefined) {
|
||||
url += `&type=${type}`;
|
||||
}
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
export const getAccessRoles = (resourceType: string) => `/api/permissions/${resourceType}/roles`;
|
||||
|
||||
export const getResourcePermissions = (resourceType: string, resourceId: string) =>
|
||||
`/api/permissions/${resourceType}/${resourceId}`;
|
||||
|
||||
export const updateResourcePermissions = (resourceType: string, resourceId: string) =>
|
||||
`/api/permissions/${resourceType}/${resourceId}`;
|
||||
|
||||
export const getEffectivePermissions = (resourceType: string, resourceId: string) =>
|
||||
`/api/permissions/${resourceType}/${resourceId}/effective`;
|
||||
|
||||
// SharePoint Graph API Token
|
||||
export const graphToken = (scopes: string) =>
|
||||
`/api/auth/graph-token?scopes=${encodeURIComponent(scopes)}`;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import * as config from './config';
|
|||
import request from './request';
|
||||
import * as s from './schemas';
|
||||
import * as r from './roles';
|
||||
import * as permissions from './accessPermissions';
|
||||
|
||||
export function revokeUserKey(name: string): Promise<unknown> {
|
||||
return request.delete(endpoints.revokeUserKey(name));
|
||||
|
|
@ -413,6 +414,14 @@ export const getAgentById = ({ agent_id }: { agent_id: string }): Promise<a.Agen
|
|||
);
|
||||
};
|
||||
|
||||
export const getExpandedAgentById = ({ agent_id }: { agent_id: string }): Promise<a.Agent> => {
|
||||
return request.get(
|
||||
endpoints.agents({
|
||||
path: `${agent_id}/expanded`,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
export const updateAgent = ({
|
||||
agent_id,
|
||||
data,
|
||||
|
|
@ -462,6 +471,80 @@ export const revertAgentVersion = ({
|
|||
version_index: number;
|
||||
}): Promise<a.Agent> => request.post(endpoints.revertAgentVersion(agent_id), { version_index });
|
||||
|
||||
/* Marketplace */
|
||||
|
||||
/**
|
||||
* Get agent categories with counts for marketplace tabs
|
||||
*/
|
||||
export const getAgentCategories = (): Promise<t.TMarketplaceCategory[]> => {
|
||||
return request.get(endpoints.agents({ path: 'marketplace/categories' }));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get promoted/top picks agents with pagination
|
||||
*/
|
||||
export const getPromotedAgents = (params: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
showAll?: string; // Add showAll parameter to get all shared agents instead of just promoted
|
||||
}): Promise<a.AgentListResponse> => {
|
||||
return request.get(
|
||||
endpoints.agents({
|
||||
path: 'marketplace/promoted',
|
||||
options: params,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all agents with pagination (for "all" category)
|
||||
*/
|
||||
export const getAllAgents = (params: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}): Promise<a.AgentListResponse> => {
|
||||
return request.get(
|
||||
endpoints.agents({
|
||||
path: 'marketplace/all',
|
||||
options: params,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get agents by category with pagination
|
||||
*/
|
||||
export const getAgentsByCategory = (params: {
|
||||
category: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}): Promise<a.AgentListResponse> => {
|
||||
const { category, ...options } = params;
|
||||
return request.get(
|
||||
endpoints.agents({
|
||||
path: `marketplace/category/${category}`,
|
||||
options,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Search agents in marketplace
|
||||
*/
|
||||
export const searchAgents = (params: {
|
||||
q: string;
|
||||
category?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}): Promise<a.AgentListResponse> => {
|
||||
return request.get(
|
||||
endpoints.agents({
|
||||
path: 'marketplace/search',
|
||||
options: params,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
/* Tools */
|
||||
|
||||
export const getAvailableAgentTools = (): Promise<s.TPlugin[]> => {
|
||||
|
|
@ -859,6 +942,38 @@ export const createMemory = (data: {
|
|||
return request.post(endpoints.memories(), data);
|
||||
};
|
||||
|
||||
export function searchPrincipals(
|
||||
params: q.PrincipalSearchParams,
|
||||
): Promise<q.PrincipalSearchResponse> {
|
||||
return request.get(endpoints.searchPrincipals(params));
|
||||
}
|
||||
|
||||
export function getAccessRoles(resourceType: string): Promise<q.AccessRolesResponse> {
|
||||
return request.get(endpoints.getAccessRoles(resourceType));
|
||||
}
|
||||
|
||||
export function getResourcePermissions(
|
||||
resourceType: string,
|
||||
resourceId: string,
|
||||
): Promise<permissions.TGetResourcePermissionsResponse> {
|
||||
return request.get(endpoints.getResourcePermissions(resourceType, resourceId));
|
||||
}
|
||||
|
||||
export function updateResourcePermissions(
|
||||
resourceType: string,
|
||||
resourceId: string,
|
||||
data: permissions.TUpdateResourcePermissionsRequest,
|
||||
): Promise<permissions.TUpdateResourcePermissionsResponse> {
|
||||
return request.put(endpoints.updateResourcePermissions(resourceType, resourceId), data);
|
||||
}
|
||||
|
||||
export function getEffectivePermissions(
|
||||
resourceType: string,
|
||||
resourceId: string,
|
||||
): Promise<permissions.TEffectivePermissionsResponse> {
|
||||
return request.get(endpoints.getEffectivePermissions(resourceType, resourceId));
|
||||
}
|
||||
|
||||
// SharePoint Graph API Token
|
||||
export function getGraphApiToken(params: q.GraphTokenParams): Promise<q.GraphTokenResponse> {
|
||||
return request.get(endpoints.graphToken(params.scopes));
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ export * from './types/mutations';
|
|||
export * from './types/queries';
|
||||
export * from './types/runs';
|
||||
export * from './types/web';
|
||||
export * from './types/graph';
|
||||
/* access permissions */
|
||||
export * from './accessPermissions';
|
||||
/* query/mutation keys */
|
||||
export * from './keys';
|
||||
/* api call helpers */
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ export enum QueryKeys {
|
|||
banner = 'banner',
|
||||
/* Memories */
|
||||
memories = 'memories',
|
||||
principalSearch = 'principalSearch',
|
||||
accessRoles = 'accessRoles',
|
||||
resourcePermissions = 'resourcePermissions',
|
||||
effectivePermissions = 'effectivePermissions',
|
||||
graphToken = 'graphToken',
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,13 @@ import { defaultOrderQuery } from '../types/assistants';
|
|||
import { MCPServerConnectionStatusResponse } from '../types/queries';
|
||||
import * as dataService from '../data-service';
|
||||
import * as m from '../types/mutations';
|
||||
import * as q from '../types/queries';
|
||||
import { QueryKeys } from '../keys';
|
||||
import * as s from '../schemas';
|
||||
import * as t from '../types';
|
||||
import * as permissions from '../accessPermissions';
|
||||
|
||||
export { hasPermissions } from '../accessPermissions';
|
||||
|
||||
export const useGetSharedMessages = (
|
||||
shareId: string,
|
||||
|
|
@ -382,6 +386,106 @@ export const useUpdateFeedbackMutation = (
|
|||
);
|
||||
};
|
||||
|
||||
export const useSearchPrincipalsQuery = (
|
||||
params: q.PrincipalSearchParams,
|
||||
config?: UseQueryOptions<q.PrincipalSearchResponse>,
|
||||
): QueryObserverResult<q.PrincipalSearchResponse> => {
|
||||
return useQuery<q.PrincipalSearchResponse>(
|
||||
[QueryKeys.principalSearch, params],
|
||||
() => dataService.searchPrincipals(params),
|
||||
{
|
||||
enabled: !!params.q && params.q.length >= 2,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
staleTime: 30000,
|
||||
...config,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export const useGetAccessRolesQuery = (
|
||||
resourceType: string,
|
||||
config?: UseQueryOptions<q.AccessRolesResponse>,
|
||||
): QueryObserverResult<q.AccessRolesResponse> => {
|
||||
return useQuery<q.AccessRolesResponse>(
|
||||
[QueryKeys.accessRoles, resourceType],
|
||||
() => dataService.getAccessRoles(resourceType),
|
||||
{
|
||||
enabled: !!resourceType,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
staleTime: 5 * 60 * 1000, // Cache for 5 minutes
|
||||
...config,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export const useGetResourcePermissionsQuery = (
|
||||
resourceType: string,
|
||||
resourceId: string,
|
||||
config?: UseQueryOptions<permissions.TGetResourcePermissionsResponse>,
|
||||
): QueryObserverResult<permissions.TGetResourcePermissionsResponse> => {
|
||||
return useQuery<permissions.TGetResourcePermissionsResponse>(
|
||||
[QueryKeys.resourcePermissions, resourceType, resourceId],
|
||||
() => dataService.getResourcePermissions(resourceType, resourceId),
|
||||
{
|
||||
enabled: !!resourceType && !!resourceId,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
staleTime: 2 * 60 * 1000, // Cache for 2 minutes
|
||||
...config,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export const useUpdateResourcePermissionsMutation = (): UseMutationResult<
|
||||
permissions.TUpdateResourcePermissionsResponse,
|
||||
Error,
|
||||
{
|
||||
resourceType: string;
|
||||
resourceId: string;
|
||||
data: permissions.TUpdateResourcePermissionsRequest;
|
||||
}
|
||||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ resourceType, resourceId, data }) =>
|
||||
dataService.updateResourcePermissions(resourceType, resourceId, data),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [QueryKeys.accessRoles, variables.resourceType],
|
||||
});
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [QueryKeys.resourcePermissions, variables.resourceType, variables.resourceId],
|
||||
});
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [QueryKeys.effectivePermissions, variables.resourceType, variables.resourceId],
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetEffectivePermissionsQuery = (
|
||||
resourceType: string,
|
||||
resourceId: string,
|
||||
config?: UseQueryOptions<permissions.TEffectivePermissionsResponse>,
|
||||
): QueryObserverResult<permissions.TEffectivePermissionsResponse> => {
|
||||
return useQuery<permissions.TEffectivePermissionsResponse>({
|
||||
queryKey: [QueryKeys.effectivePermissions, resourceType, resourceId],
|
||||
queryFn: () => dataService.getEffectivePermissions(resourceType, resourceId),
|
||||
enabled: !!resourceType && !!resourceId,
|
||||
refetchOnWindowFocus: false,
|
||||
staleTime: 30000,
|
||||
...config,
|
||||
});
|
||||
};
|
||||
|
||||
export const useMCPServerConnectionStatusQuery = (
|
||||
serverName: string,
|
||||
config?: UseQueryOptions<MCPServerConnectionStatusResponse>,
|
||||
|
|
|
|||
|
|
@ -177,11 +177,17 @@ export const defaultAgentFormValues = {
|
|||
provider: {},
|
||||
projectIds: [],
|
||||
artifacts: '',
|
||||
/** @deprecated Use ACL permissions instead */
|
||||
isCollaborative: false,
|
||||
recursion_limit: undefined,
|
||||
[Tools.execute_code]: false,
|
||||
[Tools.file_search]: false,
|
||||
[Tools.web_search]: false,
|
||||
category: 'general',
|
||||
support_contact: {
|
||||
name: '',
|
||||
email: '',
|
||||
},
|
||||
};
|
||||
|
||||
export const ImageVisionTool: FunctionTool = {
|
||||
|
|
|
|||
|
|
@ -161,6 +161,11 @@ export type TCategory = {
|
|||
label: string;
|
||||
};
|
||||
|
||||
export type TMarketplaceCategory = TCategory & {
|
||||
count: number;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export type TError = {
|
||||
message: string;
|
||||
code?: number | string;
|
||||
|
|
|
|||
|
|
@ -198,6 +198,7 @@ export interface AgentFileResource extends AgentBaseResource {
|
|||
}
|
||||
|
||||
export type Agent = {
|
||||
_id?: string;
|
||||
id: string;
|
||||
name: string | null;
|
||||
author?: string | null;
|
||||
|
|
@ -217,6 +218,7 @@ export type Agent = {
|
|||
model: string | null;
|
||||
model_parameters: AgentModelParameters;
|
||||
conversation_starters?: string[];
|
||||
/** @deprecated Use ACL permissions instead */
|
||||
isCollaborative?: boolean;
|
||||
tool_resources?: AgentToolResources;
|
||||
agent_ids?: string[];
|
||||
|
|
@ -224,6 +226,7 @@ export type Agent = {
|
|||
hide_sequential_outputs?: boolean;
|
||||
artifacts?: ArtifactModes;
|
||||
recursion_limit?: number;
|
||||
isPublic?: boolean;
|
||||
version?: number;
|
||||
};
|
||||
|
||||
|
|
|
|||
145
packages/data-provider/src/types/graph.ts
Normal file
145
packages/data-provider/src/types/graph.ts
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
* Microsoft Graph API type definitions
|
||||
* Based on Microsoft Graph REST API v1.0 documentation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Person type information from Microsoft Graph People API
|
||||
*/
|
||||
export interface TGraphPersonType {
|
||||
/** Classification of the entity: "Person" or "Group" */
|
||||
class: 'Person' | 'Group';
|
||||
/** Specific subtype: e.g., "OrganizationUser", "UnifiedGroup" */
|
||||
subclass: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scored email address from Microsoft Graph People API
|
||||
*/
|
||||
export interface TGraphScoredEmailAddress {
|
||||
/** Email address */
|
||||
address: string;
|
||||
/** Relevance score (0.0 to 1.0) */
|
||||
relevanceScore: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Phone number from Microsoft Graph API
|
||||
*/
|
||||
export interface TGraphPhone {
|
||||
/** Type of phone number */
|
||||
type: string;
|
||||
/** Phone number */
|
||||
number: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Person/Contact result from Microsoft Graph /me/people endpoint
|
||||
*/
|
||||
export interface TGraphPerson {
|
||||
/** Unique identifier */
|
||||
id: string;
|
||||
/** Display name */
|
||||
displayName: string;
|
||||
/** Given name (first name) */
|
||||
givenName?: string;
|
||||
/** Surname (last name) */
|
||||
surname?: string;
|
||||
/** User principal name */
|
||||
userPrincipalName?: string;
|
||||
/** Job title */
|
||||
jobTitle?: string;
|
||||
/** Department */
|
||||
department?: string;
|
||||
/** Company name */
|
||||
companyName?: string;
|
||||
/** Primary email address */
|
||||
mail?: string;
|
||||
/** Scored email addresses with relevance */
|
||||
scoredEmailAddresses?: TGraphScoredEmailAddress[];
|
||||
/** Person type classification */
|
||||
personType?: TGraphPersonType;
|
||||
/** Phone numbers */
|
||||
phones?: TGraphPhone[];
|
||||
}
|
||||
|
||||
/**
|
||||
* User result from Microsoft Graph /users endpoint
|
||||
*/
|
||||
export interface TGraphUser {
|
||||
/** Unique identifier */
|
||||
id: string;
|
||||
/** Display name */
|
||||
displayName: string;
|
||||
/** Given name (first name) */
|
||||
givenName?: string;
|
||||
/** Surname (last name) */
|
||||
surname?: string;
|
||||
/** User principal name */
|
||||
userPrincipalName: string;
|
||||
/** Primary email address */
|
||||
mail?: string;
|
||||
/** Job title */
|
||||
jobTitle?: string;
|
||||
/** Department */
|
||||
department?: string;
|
||||
/** Office location */
|
||||
officeLocation?: string;
|
||||
/** Business phone numbers */
|
||||
businessPhones?: string[];
|
||||
/** Mobile phone number */
|
||||
mobilePhone?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group result from Microsoft Graph /groups endpoint
|
||||
*/
|
||||
export interface TGraphGroup {
|
||||
/** Unique identifier */
|
||||
id: string;
|
||||
/** Display name */
|
||||
displayName: string;
|
||||
/** Group email address */
|
||||
mail?: string;
|
||||
/** Mail nickname */
|
||||
mailNickname?: string;
|
||||
/** Group description */
|
||||
description?: string;
|
||||
/** Group types (e.g., ["Unified"] for Microsoft 365 groups) */
|
||||
groupTypes?: string[];
|
||||
/** Whether group is mail-enabled */
|
||||
mailEnabled?: boolean;
|
||||
/** Whether group is security-enabled */
|
||||
securityEnabled?: boolean;
|
||||
/** Resource provisioning options */
|
||||
resourceProvisioningOptions?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Response wrapper for Microsoft Graph API list endpoints
|
||||
*/
|
||||
export interface TGraphListResponse<T> {
|
||||
/** Array of results */
|
||||
value: T[];
|
||||
/** OData context */
|
||||
'@odata.context'?: string;
|
||||
/** Next page link */
|
||||
'@odata.nextLink'?: string;
|
||||
/** Count of results (if requested) */
|
||||
'@odata.count'?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response from /me/people endpoint
|
||||
*/
|
||||
export type TGraphPeopleResponse = TGraphListResponse<TGraphPerson>;
|
||||
|
||||
/**
|
||||
* Response from /users endpoint
|
||||
*/
|
||||
export type TGraphUsersResponse = TGraphListResponse<TGraphUser>;
|
||||
|
||||
/**
|
||||
* Response from /groups endpoint
|
||||
*/
|
||||
export type TGraphGroupsResponse = TGraphListResponse<TGraphGroup>;
|
||||
|
|
@ -125,6 +125,47 @@ export type MemoriesResponse = {
|
|||
usagePercentage: number | null;
|
||||
};
|
||||
|
||||
export type PrincipalSearchParams = {
|
||||
q: string;
|
||||
limit?: number;
|
||||
type?: 'user' | 'group';
|
||||
};
|
||||
|
||||
export type PrincipalSearchResult = {
|
||||
id?: string | null;
|
||||
type: 'user' | 'group';
|
||||
name: string;
|
||||
email?: string;
|
||||
username?: string;
|
||||
avatar?: string;
|
||||
provider?: string;
|
||||
source: 'local' | 'entra';
|
||||
memberCount?: number;
|
||||
description?: string;
|
||||
idOnTheSource?: string;
|
||||
};
|
||||
|
||||
export type PrincipalSearchResponse = {
|
||||
query: string;
|
||||
limit: number;
|
||||
type?: 'user' | 'group';
|
||||
results: PrincipalSearchResult[];
|
||||
count: number;
|
||||
sources: {
|
||||
local: number;
|
||||
entra: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type AccessRole = {
|
||||
accessRoleId: string;
|
||||
name: string;
|
||||
description: string;
|
||||
permBits: number;
|
||||
};
|
||||
|
||||
export type AccessRolesResponse = AccessRole[];
|
||||
|
||||
export interface MCPServerStatus {
|
||||
requiresOAuth: boolean;
|
||||
connectionState: 'disconnected' | 'connecting' | 'connected' | 'error';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue