mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-03-09 17:42:38 +01:00
147 lines
4.2 KiB
TypeScript
147 lines
4.2 KiB
TypeScript
|
|
import { createMethods } from '@librechat/data-schemas';
|
||
|
|
import { ResourceType, PermissionBits, hasPermissions } from 'librechat-data-provider';
|
||
|
|
import type { AllMethods, IUser } from '@librechat/data-schemas';
|
||
|
|
import type { Types } from 'mongoose';
|
||
|
|
|
||
|
|
export interface ApiKeyServiceDependencies {
|
||
|
|
validateAgentApiKey: AllMethods['validateAgentApiKey'];
|
||
|
|
createAgentApiKey: AllMethods['createAgentApiKey'];
|
||
|
|
listAgentApiKeys: AllMethods['listAgentApiKeys'];
|
||
|
|
deleteAgentApiKey: AllMethods['deleteAgentApiKey'];
|
||
|
|
getAgentApiKeyById: AllMethods['getAgentApiKeyById'];
|
||
|
|
findUser: (query: { _id: string | Types.ObjectId }) => Promise<IUser | null>;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface RemoteAgentAccessResult {
|
||
|
|
hasAccess: boolean;
|
||
|
|
permissions: number;
|
||
|
|
agent: { _id: Types.ObjectId; [key: string]: unknown } | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export class AgentApiKeyService {
|
||
|
|
private deps: ApiKeyServiceDependencies;
|
||
|
|
|
||
|
|
constructor(deps: ApiKeyServiceDependencies) {
|
||
|
|
this.deps = deps;
|
||
|
|
}
|
||
|
|
|
||
|
|
async validateApiKey(apiKey: string): Promise<{
|
||
|
|
userId: Types.ObjectId;
|
||
|
|
keyId: Types.ObjectId;
|
||
|
|
} | null> {
|
||
|
|
return this.deps.validateAgentApiKey(apiKey);
|
||
|
|
}
|
||
|
|
|
||
|
|
async createApiKey(params: {
|
||
|
|
userId: string | Types.ObjectId;
|
||
|
|
name: string;
|
||
|
|
expiresAt?: Date | null;
|
||
|
|
}) {
|
||
|
|
return this.deps.createAgentApiKey(params);
|
||
|
|
}
|
||
|
|
|
||
|
|
async listApiKeys(userId: string | Types.ObjectId) {
|
||
|
|
return this.deps.listAgentApiKeys(userId);
|
||
|
|
}
|
||
|
|
|
||
|
|
async deleteApiKey(keyId: string | Types.ObjectId, userId: string | Types.ObjectId) {
|
||
|
|
return this.deps.deleteAgentApiKey(keyId, userId);
|
||
|
|
}
|
||
|
|
|
||
|
|
async getApiKeyById(keyId: string | Types.ObjectId, userId: string | Types.ObjectId) {
|
||
|
|
return this.deps.getAgentApiKeyById(keyId, userId);
|
||
|
|
}
|
||
|
|
|
||
|
|
async getUserFromApiKey(apiKey: string): Promise<IUser | null> {
|
||
|
|
const keyValidation = await this.validateApiKey(apiKey);
|
||
|
|
if (!keyValidation) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return this.deps.findUser({ _id: keyValidation.userId });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function createApiKeyServiceDependencies(
|
||
|
|
mongoose: typeof import('mongoose'),
|
||
|
|
): ApiKeyServiceDependencies {
|
||
|
|
const methods = createMethods(mongoose);
|
||
|
|
return {
|
||
|
|
validateAgentApiKey: methods.validateAgentApiKey,
|
||
|
|
createAgentApiKey: methods.createAgentApiKey,
|
||
|
|
listAgentApiKeys: methods.listAgentApiKeys,
|
||
|
|
deleteAgentApiKey: methods.deleteAgentApiKey,
|
||
|
|
getAgentApiKeyById: methods.getAgentApiKeyById,
|
||
|
|
findUser: methods.findUser,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface GetRemoteAgentPermissionsDeps {
|
||
|
|
getEffectivePermissions: (params: {
|
||
|
|
userId: string;
|
||
|
|
role?: string;
|
||
|
|
resourceType: ResourceType;
|
||
|
|
resourceId: string | Types.ObjectId;
|
||
|
|
}) => Promise<number>;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** AGENT owners automatically have full REMOTE_AGENT permissions */
|
||
|
|
export async function getRemoteAgentPermissions(
|
||
|
|
deps: GetRemoteAgentPermissionsDeps,
|
||
|
|
userId: string,
|
||
|
|
role: string | undefined,
|
||
|
|
resourceId: string | Types.ObjectId,
|
||
|
|
): Promise<number> {
|
||
|
|
const agentPerms = await deps.getEffectivePermissions({
|
||
|
|
userId,
|
||
|
|
role,
|
||
|
|
resourceType: ResourceType.AGENT,
|
||
|
|
resourceId,
|
||
|
|
});
|
||
|
|
|
||
|
|
if (hasPermissions(agentPerms, PermissionBits.SHARE)) {
|
||
|
|
return PermissionBits.VIEW | PermissionBits.EDIT | PermissionBits.DELETE | PermissionBits.SHARE;
|
||
|
|
}
|
||
|
|
|
||
|
|
return deps.getEffectivePermissions({
|
||
|
|
userId,
|
||
|
|
role,
|
||
|
|
resourceType: ResourceType.REMOTE_AGENT,
|
||
|
|
resourceId,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function checkRemoteAgentAccess(params: {
|
||
|
|
userId: string;
|
||
|
|
role?: string;
|
||
|
|
agentId: string;
|
||
|
|
getAgent: (query: {
|
||
|
|
id: string;
|
||
|
|
}) => Promise<{ _id: Types.ObjectId; [key: string]: unknown } | null>;
|
||
|
|
getEffectivePermissions: (params: {
|
||
|
|
userId: string;
|
||
|
|
role?: string;
|
||
|
|
resourceType: ResourceType;
|
||
|
|
resourceId: string | Types.ObjectId;
|
||
|
|
}) => Promise<number>;
|
||
|
|
}): Promise<RemoteAgentAccessResult> {
|
||
|
|
const { userId, role, agentId, getAgent, getEffectivePermissions } = params;
|
||
|
|
|
||
|
|
const agent = await getAgent({ id: agentId });
|
||
|
|
|
||
|
|
if (!agent) {
|
||
|
|
return { hasAccess: false, permissions: 0, agent: null };
|
||
|
|
}
|
||
|
|
|
||
|
|
const permissions = await getRemoteAgentPermissions(
|
||
|
|
{ getEffectivePermissions },
|
||
|
|
userId,
|
||
|
|
role,
|
||
|
|
agent._id,
|
||
|
|
);
|
||
|
|
|
||
|
|
const hasAccess = hasPermissions(permissions, PermissionBits.VIEW);
|
||
|
|
|
||
|
|
return { hasAccess, permissions, agent };
|
||
|
|
}
|