mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 06:00:56 +02:00
🛂 feat: Role as Permission Principal Type
WIP: Role as Permission Principal Type WIP: add user role check optimization to user principal check, update type comparisons WIP: cover edge cases for string vs ObjectId handling in permission granting and checking chore: Update people picker access middleware to use PrincipalType constants feat: Enhance people picker access control to include roles permissions chore: add missing default role schema values for people picker perms, cleanup typing feat: Enhance PeoplePicker component with role-specific UI and localization updates chore: Add missing `VIEW_ROLES` permission to role schema
This commit is contained in:
parent
28d63dab71
commit
39346d6b8e
49 changed files with 2879 additions and 258 deletions
|
@ -17,6 +17,7 @@ export enum PrincipalType {
|
|||
USER = 'user',
|
||||
GROUP = 'group',
|
||||
PUBLIC = 'public',
|
||||
ROLE = 'role',
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,6 +26,7 @@ export enum PrincipalType {
|
|||
export enum PrincipalModel {
|
||||
USER = 'User',
|
||||
GROUP = 'Group',
|
||||
ROLE = 'Role',
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,16 +76,16 @@ export enum AccessRoleIds {
|
|||
// ===== ZOD SCHEMAS =====
|
||||
|
||||
/**
|
||||
* Principal schema - represents a user, group, or public access
|
||||
* Principal schema - represents a user, group, role, or public access
|
||||
*/
|
||||
export const principalSchema = z.object({
|
||||
type: z.nativeEnum(PrincipalType),
|
||||
id: z.string().optional(), // undefined for 'public' type
|
||||
id: z.string().optional(), // undefined for 'public' type, role name for 'role' 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
|
||||
description: z.string().optional(), // for group and role types
|
||||
idOnTheSource: z.string().optional(), // Entra ID for users/groups
|
||||
accessRoleId: z.nativeEnum(AccessRoleIds).optional(), // Access role ID for permissions
|
||||
memberCount: z.number().optional(), // for group type
|
||||
|
@ -192,7 +194,7 @@ export type TUpdateResourcePermissionsResponse = z.infer<
|
|||
export type TPrincipalSearchParams = {
|
||||
q: string; // search query (required)
|
||||
limit?: number; // max results (1-50, default 10)
|
||||
type?: 'user' | 'group'; // filter by type (optional)
|
||||
type?: PrincipalType.USER | PrincipalType.GROUP | PrincipalType.ROLE; // filter by type (optional)
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -200,7 +202,7 @@ export type TPrincipalSearchParams = {
|
|||
*/
|
||||
export type TPrincipalSearchResult = {
|
||||
id?: string | null; // null for Entra ID principals that don't exist locally yet
|
||||
type: 'user' | 'group';
|
||||
type: PrincipalType.USER | PrincipalType.GROUP | PrincipalType.ROLE;
|
||||
name: string;
|
||||
email?: string; // for users and groups
|
||||
username?: string; // for users
|
||||
|
@ -218,7 +220,7 @@ export type TPrincipalSearchResult = {
|
|||
export type TPrincipalSearchResponse = {
|
||||
query: string;
|
||||
limit: number;
|
||||
type?: 'user' | 'group';
|
||||
type?: PrincipalType.USER | PrincipalType.GROUP | PrincipalType.ROLE;
|
||||
results: TPrincipalSearchResult[];
|
||||
count: number;
|
||||
sources: {
|
||||
|
|
|
@ -538,12 +538,14 @@ export const interfaceSchema = z
|
|||
.object({
|
||||
users: z.boolean().optional(),
|
||||
groups: z.boolean().optional(),
|
||||
roles: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
user: z
|
||||
.object({
|
||||
users: z.boolean().optional(),
|
||||
groups: z.boolean().optional(),
|
||||
roles: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
|
@ -583,10 +585,12 @@ export const interfaceSchema = z
|
|||
admin: {
|
||||
users: true,
|
||||
groups: true,
|
||||
roles: true,
|
||||
},
|
||||
user: {
|
||||
users: false,
|
||||
groups: false,
|
||||
roles: false,
|
||||
},
|
||||
},
|
||||
marketplace: {
|
||||
|
|
|
@ -69,6 +69,7 @@ export enum Permissions {
|
|||
OPT_OUT = 'OPT_OUT',
|
||||
VIEW_USERS = 'VIEW_USERS',
|
||||
VIEW_GROUPS = 'VIEW_GROUPS',
|
||||
VIEW_ROLES = 'VIEW_ROLES',
|
||||
}
|
||||
|
||||
export const promptPermissionsSchema = z.object({
|
||||
|
@ -124,6 +125,7 @@ export type TWebSearchPermissions = z.infer<typeof webSearchPermissionsSchema>;
|
|||
export const peoplePickerPermissionsSchema = z.object({
|
||||
[Permissions.VIEW_USERS]: z.boolean().default(true),
|
||||
[Permissions.VIEW_GROUPS]: z.boolean().default(true),
|
||||
[Permissions.VIEW_ROLES]: z.boolean().default(true),
|
||||
});
|
||||
export type TPeoplePickerPermissions = z.infer<typeof peoplePickerPermissionsSchema>;
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ export enum SystemRoles {
|
|||
USER = 'USER',
|
||||
}
|
||||
|
||||
// The role schema now only needs to reference the permissions schema.
|
||||
export const roleSchema = z.object({
|
||||
name: z.string(),
|
||||
permissions: permissionsSchema,
|
||||
|
@ -38,7 +37,6 @@ export const roleSchema = z.object({
|
|||
|
||||
export type TRole = z.infer<typeof roleSchema>;
|
||||
|
||||
// Define default roles using the new structure.
|
||||
const defaultRolesSchema = z.object({
|
||||
[SystemRoles.ADMIN]: roleSchema.extend({
|
||||
name: z.literal(SystemRoles.ADMIN),
|
||||
|
@ -80,6 +78,7 @@ const defaultRolesSchema = z.object({
|
|||
[PermissionTypes.PEOPLE_PICKER]: peoplePickerPermissionsSchema.extend({
|
||||
[Permissions.VIEW_USERS]: z.boolean().default(true),
|
||||
[Permissions.VIEW_GROUPS]: z.boolean().default(true),
|
||||
[Permissions.VIEW_ROLES]: z.boolean().default(true),
|
||||
}),
|
||||
[PermissionTypes.MARKETPLACE]: z.object({
|
||||
[Permissions.USE]: z.boolean().default(false),
|
||||
|
@ -137,6 +136,7 @@ export const roleDefaults = defaultRolesSchema.parse({
|
|||
[PermissionTypes.PEOPLE_PICKER]: {
|
||||
[Permissions.VIEW_USERS]: true,
|
||||
[Permissions.VIEW_GROUPS]: true,
|
||||
[Permissions.VIEW_ROLES]: true,
|
||||
},
|
||||
[PermissionTypes.MARKETPLACE]: {
|
||||
[Permissions.USE]: true,
|
||||
|
@ -163,6 +163,7 @@ export const roleDefaults = defaultRolesSchema.parse({
|
|||
[PermissionTypes.PEOPLE_PICKER]: {
|
||||
[Permissions.VIEW_USERS]: false,
|
||||
[Permissions.VIEW_GROUPS]: false,
|
||||
[Permissions.VIEW_ROLES]: false,
|
||||
},
|
||||
[PermissionTypes.MARKETPLACE]: {
|
||||
[Permissions.USE]: false,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { InfiniteData } from '@tanstack/react-query';
|
||||
import type { AccessRoleIds } from '../accessPermissions';
|
||||
import type * as p from '../accessPermissions';
|
||||
import type * as a from '../types/agents';
|
||||
import type * as s from '../schemas';
|
||||
import type * as t from '../types';
|
||||
|
@ -129,28 +129,14 @@ export type MemoriesResponse = {
|
|||
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;
|
||||
type?: p.PrincipalType.USER | p.PrincipalType.GROUP | p.PrincipalType.ROLE;
|
||||
};
|
||||
|
||||
export type PrincipalSearchResponse = {
|
||||
query: string;
|
||||
limit: number;
|
||||
type?: 'user' | 'group';
|
||||
results: PrincipalSearchResult[];
|
||||
type?: p.PrincipalType.USER | p.PrincipalType.GROUP | p.PrincipalType.ROLE;
|
||||
results: p.TPrincipalSearchResult[];
|
||||
count: number;
|
||||
sources: {
|
||||
local: number;
|
||||
|
@ -159,7 +145,7 @@ export type PrincipalSearchResponse = {
|
|||
};
|
||||
|
||||
export type AccessRole = {
|
||||
accessRoleId: AccessRoleIds;
|
||||
accessRoleId: p.AccessRoleIds;
|
||||
name: string;
|
||||
description: string;
|
||||
permBits: number;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue