mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-04-07 00:15:23 +02:00
consider migration and update user
This commit is contained in:
parent
f51fe917d1
commit
b98fd221b9
4 changed files with 78 additions and 6 deletions
|
|
@ -30,18 +30,21 @@ const {
|
|||
GetModelController,
|
||||
} = require('~/server/controllers/agents/openai');
|
||||
const { getEffectivePermissions } = require('~/server/services/PermissionService');
|
||||
const { validateAgentApiKey, findUser } = require('~/models');
|
||||
const { configMiddleware } = require('~/server/middleware');
|
||||
const { getAppConfig } = require('~/server/services/Config');
|
||||
const db = require('~/models');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const apiKeyMiddleware = createRequireApiKeyAuth({ validateAgentApiKey, findUser });
|
||||
const apiKeyMiddleware = createRequireApiKeyAuth({
|
||||
validateAgentApiKey: db.validateAgentApiKey,
|
||||
findUser: db.findUser
|
||||
});
|
||||
|
||||
const requireRemoteAgentAuth = createRemoteAgentAuth({
|
||||
apiKeyMiddleware,
|
||||
findUser: db.findUser,
|
||||
updateUser: db.updateUser,
|
||||
getAppConfig,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -33,18 +33,21 @@ const {
|
|||
listModels,
|
||||
} = require('~/server/controllers/agents/responses');
|
||||
const { getEffectivePermissions } = require('~/server/services/PermissionService');
|
||||
const { validateAgentApiKey, findUser } = require('~/models');
|
||||
const { configMiddleware } = require('~/server/middleware');
|
||||
const { getAppConfig } = require('~/server/services/Config');
|
||||
const db = require('~/models');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const apiKeyMiddleware = createRequireApiKeyAuth({ validateAgentApiKey, findUser });
|
||||
const apiKeyMiddleware = createRequireApiKeyAuth({
|
||||
validateAgentApiKey: db.validateAgentApiKey,
|
||||
findUser: db.findUser
|
||||
});
|
||||
|
||||
const requireRemoteAgentAuth = createRemoteAgentAuth({
|
||||
apiKeyMiddleware,
|
||||
findUser: db.findUser,
|
||||
updateUser: db.updateUser,
|
||||
getAppConfig,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ function makeConfig(oidcOverrides?: object, apiKeyOverrides?: object): AppConfig
|
|||
function makeDeps(appConfig: AppConfig | null = makeConfig()) {
|
||||
return {
|
||||
findUser: jest.fn(),
|
||||
updateUser: jest.fn(),
|
||||
getAppConfig: jest.fn().mockResolvedValue(appConfig),
|
||||
apiKeyMiddleware: jest.fn((_req: unknown, _res: unknown, next: () => void) => next()),
|
||||
};
|
||||
|
|
@ -454,4 +455,48 @@ describe('createRemoteAgentAuth', () => {
|
|||
expect(await captureEmailArg({ sub: 's3', upn: 'upn@corp.com' })).toBe('upn@corp.com');
|
||||
});
|
||||
});
|
||||
|
||||
describe('update user and migration scenarios', () => {
|
||||
it('persists openidId binding when migration is needed', async () => {
|
||||
const mockUpdateUser = jest.fn().mockResolvedValue(undefined);
|
||||
setupOidcMocks({ sub: 'sub-new', email: 'existing@test.com' });
|
||||
(findOpenIDUser as jest.Mock).mockResolvedValue({
|
||||
user: { ...mockUser, openidId: undefined, role: 'user' },
|
||||
error: null,
|
||||
migration: true,
|
||||
});
|
||||
|
||||
const deps = { ...makeDeps(), updateUser: mockUpdateUser };
|
||||
await createRemoteAgentAuth(deps as any)(
|
||||
makeReq({ authorization: `Bearer ${FAKE_TOKEN}` }) as Request,
|
||||
makeRes().res,
|
||||
mockNext,
|
||||
);
|
||||
|
||||
expect(mockUpdateUser).toHaveBeenCalledWith(
|
||||
mockUser.id,
|
||||
expect.objectContaining({ provider: 'openid', openidId: 'sub-new' }),
|
||||
);
|
||||
expect(mockNext).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not call updateUser when migration is false and role exists', async () => {
|
||||
const mockUpdateUser = jest.fn();
|
||||
setupOidcMocks({ sub: 'sub123', email: 'agent@test.com' });
|
||||
(findOpenIDUser as jest.Mock).mockResolvedValue({
|
||||
user: { ...mockUser, role: 'user' },
|
||||
error: null,
|
||||
migration: false,
|
||||
});
|
||||
|
||||
const deps = { ...makeDeps(), updateUser: mockUpdateUser };
|
||||
await createRemoteAgentAuth(deps as any)(
|
||||
makeReq({ authorization: `Bearer ${FAKE_TOKEN}` }) as Request,
|
||||
makeRes().res,
|
||||
mockNext,
|
||||
);
|
||||
|
||||
expect(mockUpdateUser).not.toHaveBeenCalled();
|
||||
});
|
||||
})
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ import type { RequestHandler, Request, Response, NextFunction } from 'express';
|
|||
import type { JwtPayload } from 'jsonwebtoken';
|
||||
import type { AppConfig, IUser, UserMethods } from '@librechat/data-schemas';
|
||||
import type { TAgentsEndpoint } from 'librechat-data-provider';
|
||||
import { SystemRoles } from 'librechat-data-provider';
|
||||
import { isEnabled, math } from '~/utils';
|
||||
import { findOpenIDUser } from '../auth/openid';
|
||||
|
||||
export interface RemoteAgentAuthDeps {
|
||||
apiKeyMiddleware: RequestHandler;
|
||||
findUser: UserMethods['findUser'];
|
||||
updateUser: UserMethods['updateUser'];
|
||||
getAppConfig: () => Promise<AppConfig | null>;
|
||||
}
|
||||
|
||||
|
|
@ -141,8 +143,9 @@ async function resolveUser(
|
|||
token: string,
|
||||
payload: JwtPayload,
|
||||
findUser: UserMethods['findUser'],
|
||||
updateUser: UserMethods['updateUser'],
|
||||
): Promise<IUser | null> {
|
||||
const { user, error } = await findOpenIDUser({
|
||||
const { user, error, migration } = await findOpenIDUser({
|
||||
findUser,
|
||||
email: getEmail(payload),
|
||||
openidId: payload.sub ?? '',
|
||||
|
|
@ -153,6 +156,23 @@ async function resolveUser(
|
|||
if (error != null || user == null) return null;
|
||||
|
||||
user.id = String(user._id);
|
||||
|
||||
const updateData: Partial<IUser> = {};
|
||||
|
||||
if (migration) {
|
||||
updateData.provider = 'openid';
|
||||
updateData.openidId = payload.sub;
|
||||
}
|
||||
|
||||
if (!user.role) {
|
||||
user.role = SystemRoles.USER;
|
||||
updateData.role = SystemRoles.USER;
|
||||
}
|
||||
|
||||
if (Object.keys(updateData).length > 0) {
|
||||
await updateUser(user.id, updateData);
|
||||
}
|
||||
|
||||
user.federatedTokens = { access_token: token, expires_at: payload.exp };
|
||||
return user;
|
||||
}
|
||||
|
|
@ -180,6 +200,7 @@ async function resolveUser(
|
|||
export function createRemoteAgentAuth({
|
||||
apiKeyMiddleware,
|
||||
findUser,
|
||||
updateUser,
|
||||
getAppConfig,
|
||||
}: RemoteAgentAuthDeps): RequestHandler {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
|
|
@ -202,7 +223,7 @@ export function createRemoteAgentAuth({
|
|||
|
||||
try {
|
||||
const payload = await verifyOidcBearer(token, authConfig.oidc);
|
||||
const user = await resolveUser(token, payload, findUser);
|
||||
const user = await resolveUser(token, payload, findUser, updateUser);
|
||||
|
||||
if (user == null) {
|
||||
logger.warn('[remoteAgentAuth] OIDC token valid but no matching LibreChat user');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue