feat: Enhance authentication flow by integrating permissions retrieval and updating related services (#256)

This commit is contained in:
samanhappy
2025-08-05 13:45:31 +08:00
committed by GitHub
parent 63b356b8d7
commit f63f06d879
8 changed files with 41 additions and 17 deletions

View File

@@ -1,7 +1,7 @@
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'; import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
import { AuthState } from '../types'; import { AuthState } from '../types';
import * as authService from '../services/authService'; import * as authService from '../services/authService';
import { shouldSkipAuth } from '../services/configService'; import { getPublicConfig } from '../services/configService';
// Initial auth state // Initial auth state
const initialState: AuthState = { const initialState: AuthState = {
@@ -32,7 +32,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
useEffect(() => { useEffect(() => {
const loadUser = async () => { const loadUser = async () => {
// First check if authentication should be skipped // First check if authentication should be skipped
const skipAuth = await shouldSkipAuth(); const { skipAuth, permissions } = await getPublicConfig();
if (skipAuth) { if (skipAuth) {
// If authentication is disabled, set user as authenticated with a dummy user // If authentication is disabled, set user as authenticated with a dummy user
@@ -42,6 +42,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
user: { user: {
username: 'guest', username: 'guest',
isAdmin: true, isAdmin: true,
permissions,
}, },
error: null, error: null,
}); });

View File

@@ -26,6 +26,7 @@ export interface PublicConfigResponse {
success: boolean; success: boolean;
data?: { data?: {
skipAuth?: boolean; skipAuth?: boolean;
permissions?: any;
}; };
message?: string; message?: string;
} }
@@ -41,7 +42,7 @@ export interface SystemConfigResponse {
/** /**
* Get public configuration (skipAuth setting) without authentication * Get public configuration (skipAuth setting) without authentication
*/ */
export const getPublicConfig = async (): Promise<{ skipAuth: boolean }> => { export const getPublicConfig = async (): Promise<{ skipAuth: boolean; permissions?: any }> => {
try { try {
const basePath = getBasePath(); const basePath = getBasePath();
const response = await fetchWithInterceptors(`${basePath}/public-config`, { const response = await fetchWithInterceptors(`${basePath}/public-config`, {
@@ -53,7 +54,7 @@ export const getPublicConfig = async (): Promise<{ skipAuth: boolean }> => {
if (response.ok) { if (response.ok) {
const data: PublicConfigResponse = await response.json(); const data: PublicConfigResponse = await response.json();
return { skipAuth: data.data?.skipAuth === true }; return { skipAuth: data.data?.skipAuth === true, permissions: data.data?.permissions || {} };
} }
return { skipAuth: false }; return { skipAuth: false };

View File

@@ -429,7 +429,7 @@
"edit": "编辑用户", "edit": "编辑用户",
"delete": "删除用户", "delete": "删除用户",
"create": "创建", "create": "创建",
"update": "用户", "update": "更新",
"username": "用户名", "username": "用户名",
"password": "密码", "password": "密码",
"newPassword": "新密码", "newPassword": "新密码",

View File

@@ -1,6 +1,6 @@
import dotenv from 'dotenv'; import dotenv from 'dotenv';
import fs from 'fs'; import fs from 'fs';
import { McpSettings } from '../types/index.js'; import { McpSettings, IUser } from '../types/index.js';
import { getConfigFilePath } from '../utils/path.js'; import { getConfigFilePath } from '../utils/path.js';
import { getPackageVersion } from '../utils/version.js'; import { getPackageVersion } from '../utils/version.js';
import { getDataService } from '../services/services.js'; import { getDataService } from '../services/services.js';
@@ -54,14 +54,14 @@ export const loadOriginalSettings = (): McpSettings => {
} }
}; };
export const loadSettings = (): McpSettings => { export const loadSettings = (user?: IUser): McpSettings => {
return dataService.filterSettings!(loadOriginalSettings()); return dataService.filterSettings!(loadOriginalSettings(), user);
}; };
export const saveSettings = (settings: McpSettings): boolean => { export const saveSettings = (settings: McpSettings, user?: IUser): boolean => {
const settingsPath = getSettingsPath(); const settingsPath = getSettingsPath();
try { try {
const mergedSettings = dataService.mergeSettings!(loadOriginalSettings(), settings); const mergedSettings = dataService.mergeSettings!(loadOriginalSettings(), settings, user);
fs.writeFileSync(settingsPath, JSON.stringify(mergedSettings, null, 2), 'utf8'); fs.writeFileSync(settingsPath, JSON.stringify(mergedSettings, null, 2), 'utf8');
// Update cache after successful save // Update cache after successful save

View File

@@ -1,6 +1,11 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import config from '../config/index.js'; import config from '../config/index.js';
import { loadSettings } from '../config/index.js'; import { loadSettings } from '../config/index.js';
import { getDataService } from '../services/services.js';
import { DataService } from '../services/dataService.js';
import { IUser } from '../types/index.js';
const dataService: DataService = getDataService();
/** /**
* Get runtime configuration for frontend * Get runtime configuration for frontend
@@ -38,6 +43,15 @@ export const getPublicConfig = (req: Request, res: Response): void => {
try { try {
const settings = loadSettings(); const settings = loadSettings();
const skipAuth = settings.systemConfig?.routing?.skipAuth || false; const skipAuth = settings.systemConfig?.routing?.skipAuth || false;
let permissions = {};
if (skipAuth) {
const user: IUser = {
username: 'guest',
password: '',
isAdmin: true,
};
permissions = dataService.getPermissions(user);
}
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
res.setHeader('Pragma', 'no-cache'); res.setHeader('Pragma', 'no-cache');
@@ -47,6 +61,7 @@ export const getPublicConfig = (req: Request, res: Response): void => {
success: true, success: true,
data: { data: {
skipAuth, skipAuth,
permissions,
}, },
}); });
} catch (error) { } catch (error) {

View File

@@ -506,6 +506,7 @@ export const updateToolDescription = async (req: Request, res: Response): Promis
export const updateSystemConfig = (req: Request, res: Response): void => { export const updateSystemConfig = (req: Request, res: Response): void => {
try { try {
const { routing, install, smartRouting } = req.body; const { routing, install, smartRouting } = req.body;
const currentUser = (req as any).user;
if ( if (
(!routing || (!routing ||
@@ -675,7 +676,7 @@ export const updateSystemConfig = (req: Request, res: Response): void => {
needsSync = (!wasSmartRoutingEnabled && isNowEnabled) || (isNowEnabled && hasConfigChanged); needsSync = (!wasSmartRoutingEnabled && isNowEnabled) || (isNowEnabled && hasConfigChanged);
} }
if (saveSettings(settings)) { if (saveSettings(settings, currentUser)) {
res.json({ res.json({
success: true, success: true,
data: settings.systemConfig, data: settings.systemConfig,

View File

@@ -9,9 +9,15 @@ import {
getUserCount, getUserCount,
getAdminCount, getAdminCount,
} from '../services/userService.js'; } from '../services/userService.js';
import { loadSettings } from '../config/index.js';
// Admin permission check middleware function // Admin permission check middleware function
const requireAdmin = (req: Request, res: Response): boolean => { const requireAdmin = (req: Request, res: Response): boolean => {
const settings = loadSettings();
if (settings.systemConfig?.routing?.skipAuth) {
return true;
}
const user = (req as any).user; const user = (req as any).user;
if (!user || !user.isAdmin) { if (!user || !user.isAdmin) {
res.status(403).json({ res.status(403).json({

View File

@@ -2,9 +2,9 @@ import { IUser, McpSettings } from '../types/index.js';
export interface DataService { export interface DataService {
foo(): void; foo(): void;
filterData(data: any[]): any[]; filterData(data: any[], user?: IUser): any[];
filterSettings(settings: McpSettings): McpSettings; filterSettings(settings: McpSettings, user?: IUser): McpSettings;
mergeSettings(all: McpSettings, newSettings: McpSettings): McpSettings; mergeSettings(all: McpSettings, newSettings: McpSettings, user?: IUser): McpSettings;
getPermissions(user: IUser): string[]; getPermissions(user: IUser): string[];
} }
@@ -13,15 +13,15 @@ export class DataServiceImpl implements DataService {
console.log('default implementation'); console.log('default implementation');
} }
filterData(data: any[]): any[] { filterData(data: any[], _user?: IUser): any[] {
return data; return data;
} }
filterSettings(settings: McpSettings): McpSettings { filterSettings(settings: McpSettings, _user?: IUser): McpSettings {
return settings; return settings;
} }
mergeSettings(all: McpSettings, newSettings: McpSettings): McpSettings { mergeSettings(all: McpSettings, newSettings: McpSettings, _user?: IUser): McpSettings {
return newSettings; return newSettings;
} }