Add custom access type for bearer keys to support combined group and server scoping (#530)

Co-authored-by: samanhappy <samanhappy@gmail.com>
This commit is contained in:
Copilot
2025-12-27 16:16:50 +08:00
committed by GitHub
parent b00e1c81fc
commit ab338e80a7
10 changed files with 216 additions and 76 deletions

View File

@@ -57,7 +57,7 @@ export const createBearerKey = async (req: Request, res: Response): Promise<void
return;
}
if (!accessType || !['all', 'groups', 'servers'].includes(accessType)) {
if (!accessType || !['all', 'groups', 'servers', 'custom'].includes(accessType)) {
res.status(400).json({ success: false, message: 'Invalid accessType' });
return;
}
@@ -104,7 +104,7 @@ export const updateBearerKey = async (req: Request, res: Response): Promise<void
if (token !== undefined) updates.token = token;
if (enabled !== undefined) updates.enabled = enabled;
if (accessType !== undefined) {
if (!['all', 'groups', 'servers'].includes(accessType)) {
if (!['all', 'groups', 'servers', 'custom'].includes(accessType)) {
res.status(400).json({ success: false, message: 'Invalid accessType' });
return;
}

View File

@@ -25,7 +25,7 @@ export class BearerKey {
enabled: boolean;
@Column({ type: 'varchar', length: 20, default: 'all' })
accessType: 'all' | 'groups' | 'servers';
accessType: 'all' | 'groups' | 'servers' | 'custom';
@Column({ type: 'simple-json', nullable: true })
allowedGroups?: string[];

View File

@@ -88,6 +88,29 @@ const isBearerKeyAllowedForRequest = async (req: Request, key: BearerKey): Promi
return groupServerNames.some((name) => allowedServers.includes(name));
}
if (key.accessType === 'custom') {
// For custom-scoped keys, check if the group is allowed OR if any server in the group is allowed
const allowedGroups = key.allowedGroups || [];
const allowedServers = key.allowedServers || [];
// Check if the group itself is allowed
const groupAllowed =
allowedGroups.includes(matchedGroup.name) || allowedGroups.includes(matchedGroup.id);
if (groupAllowed) {
return true;
}
// Check if any server in the group is allowed
if (allowedServers.length > 0 && Array.isArray(matchedGroup.servers)) {
const groupServerNames = matchedGroup.servers.map((server) =>
typeof server === 'string' ? server : server.name,
);
return groupServerNames.some((name) => allowedServers.includes(name));
}
return false;
}
// Unknown accessType with matched group
return false;
}
@@ -102,8 +125,8 @@ const isBearerKeyAllowedForRequest = async (req: Request, key: BearerKey): Promi
return false;
}
if (key.accessType === 'servers') {
// For server-scoped keys, check if the server is in allowedServers
if (key.accessType === 'servers' || key.accessType === 'custom') {
// For server-scoped or custom-scoped keys, check if the server is in allowedServers
const allowedServers = key.allowedServers || [];
return allowedServers.includes(matchedServer.name);
}

View File

@@ -244,7 +244,7 @@ export interface OAuthServerConfig {
}
// Bearer authentication key configuration
export type BearerKeyAccessType = 'all' | 'groups' | 'servers';
export type BearerKeyAccessType = 'all' | 'groups' | 'servers' | 'custom';
export interface BearerKey {
id: string; // Unique identifier for the key
@@ -252,8 +252,8 @@ export interface BearerKey {
token: string; // Bearer token value
enabled: boolean; // Whether this key is enabled
accessType: BearerKeyAccessType; // Access scope type
allowedGroups?: string[]; // Allowed group names when accessType === 'groups'
allowedServers?: string[]; // Allowed server names when accessType === 'servers'
allowedGroups?: string[]; // Allowed group names when accessType === 'groups' or 'custom'
allowedServers?: string[]; // Allowed server names when accessType === 'servers' or 'custom'
}
// Represents the settings for MCP servers