mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-16 07:28:09 +01:00
🔐 feat: Granular Role-based Permissions + Entra ID Group Discovery (#7804)
This commit is contained in:
parent
6c9a29b6cf
commit
f55cdc9b7f
99 changed files with 11321 additions and 621 deletions
31
packages/data-schemas/src/schema/accessRole.ts
Normal file
31
packages/data-schemas/src/schema/accessRole.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { Schema } from 'mongoose';
|
||||
import type { IAccessRole } from '~/types';
|
||||
|
||||
const accessRoleSchema = new Schema<IAccessRole>(
|
||||
{
|
||||
accessRoleId: {
|
||||
type: String,
|
||||
required: true,
|
||||
index: true,
|
||||
unique: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
description: String,
|
||||
resourceType: {
|
||||
type: String,
|
||||
enum: ['agent', 'project', 'file'],
|
||||
required: true,
|
||||
default: 'agent',
|
||||
},
|
||||
permBits: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{ timestamps: true },
|
||||
);
|
||||
|
||||
export default accessRoleSchema;
|
||||
65
packages/data-schemas/src/schema/aclEntry.ts
Normal file
65
packages/data-schemas/src/schema/aclEntry.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { Schema } from 'mongoose';
|
||||
import type { IAclEntry } from '~/types';
|
||||
|
||||
const aclEntrySchema = new Schema<IAclEntry>(
|
||||
{
|
||||
principalType: {
|
||||
type: String,
|
||||
enum: ['user', 'group', 'public'],
|
||||
required: true,
|
||||
},
|
||||
principalId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
refPath: 'principalModel',
|
||||
required: function (this: IAclEntry) {
|
||||
return this.principalType !== 'public';
|
||||
},
|
||||
index: true,
|
||||
},
|
||||
principalModel: {
|
||||
type: String,
|
||||
enum: ['User', 'Group'],
|
||||
required: function (this: IAclEntry) {
|
||||
return this.principalType !== 'public';
|
||||
},
|
||||
},
|
||||
resourceType: {
|
||||
type: String,
|
||||
enum: ['agent', 'project', 'file'],
|
||||
required: true,
|
||||
},
|
||||
resourceId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
required: true,
|
||||
index: true,
|
||||
},
|
||||
permBits: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
roleId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'AccessRole',
|
||||
},
|
||||
inheritedFrom: {
|
||||
type: Schema.Types.ObjectId,
|
||||
sparse: true,
|
||||
index: true,
|
||||
},
|
||||
grantedBy: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
},
|
||||
grantedAt: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
},
|
||||
},
|
||||
{ timestamps: true },
|
||||
);
|
||||
|
||||
aclEntrySchema.index({ principalId: 1, principalType: 1, resourceType: 1, resourceId: 1 });
|
||||
aclEntrySchema.index({ resourceId: 1, principalType: 1, principalId: 1 });
|
||||
aclEntrySchema.index({ principalId: 1, permBits: 1, resourceType: 1 });
|
||||
|
||||
export default aclEntrySchema;
|
||||
|
|
@ -98,4 +98,6 @@ const agentSchema = new Schema<IAgent>(
|
|||
},
|
||||
);
|
||||
|
||||
agentSchema.index({ updatedAt: -1, _id: 1 });
|
||||
|
||||
export default agentSchema;
|
||||
|
|
|
|||
56
packages/data-schemas/src/schema/group.ts
Normal file
56
packages/data-schemas/src/schema/group.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { Schema } from 'mongoose';
|
||||
import type { IGroup } from '~/types';
|
||||
|
||||
const groupSchema = new Schema<IGroup>(
|
||||
{
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
index: true,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: false,
|
||||
index: true,
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
memberIds: [
|
||||
{
|
||||
type: String,
|
||||
},
|
||||
],
|
||||
source: {
|
||||
type: String,
|
||||
enum: ['local', 'entra'],
|
||||
default: 'local',
|
||||
},
|
||||
/** External ID (e.g., Entra ID) */
|
||||
idOnTheSource: {
|
||||
type: String,
|
||||
sparse: true,
|
||||
index: true,
|
||||
required: function (this: IGroup) {
|
||||
return this.source !== 'local';
|
||||
},
|
||||
},
|
||||
},
|
||||
{ timestamps: true },
|
||||
);
|
||||
|
||||
groupSchema.index(
|
||||
{ idOnTheSource: 1, source: 1 },
|
||||
{
|
||||
unique: true,
|
||||
partialFilterExpression: { idOnTheSource: { $exists: true } },
|
||||
},
|
||||
);
|
||||
groupSchema.index({ memberIds: 1 });
|
||||
|
||||
export default groupSchema;
|
||||
|
|
@ -138,6 +138,11 @@ const userSchema = new Schema<IUser>(
|
|||
},
|
||||
default: {},
|
||||
},
|
||||
/** Field for external source identification (for consistency with TPrincipal schema) */
|
||||
idOnTheSource: {
|
||||
type: String,
|
||||
sparse: true,
|
||||
},
|
||||
},
|
||||
{ timestamps: true },
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue