mirror of
https://github.com/samanhappy/mcphub.git
synced 2026-01-01 04:08:52 -05:00
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:
@@ -25,7 +25,7 @@ interface BearerKeyRowProps {
|
|||||||
name: string;
|
name: string;
|
||||||
token: string;
|
token: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
accessType: 'all' | 'groups' | 'servers';
|
accessType: 'all' | 'groups' | 'servers' | 'custom';
|
||||||
allowedGroups: string;
|
allowedGroups: string;
|
||||||
allowedServers: string;
|
allowedServers: string;
|
||||||
},
|
},
|
||||||
@@ -47,7 +47,7 @@ const BearerKeyRow: React.FC<BearerKeyRowProps> = ({
|
|||||||
const [name, setName] = useState(keyData.name);
|
const [name, setName] = useState(keyData.name);
|
||||||
const [token, setToken] = useState(keyData.token);
|
const [token, setToken] = useState(keyData.token);
|
||||||
const [enabled, setEnabled] = useState<boolean>(keyData.enabled);
|
const [enabled, setEnabled] = useState<boolean>(keyData.enabled);
|
||||||
const [accessType, setAccessType] = useState<'all' | 'groups' | 'servers'>(
|
const [accessType, setAccessType] = useState<'all' | 'groups' | 'servers' | 'custom'>(
|
||||||
keyData.accessType || 'all',
|
keyData.accessType || 'all',
|
||||||
);
|
);
|
||||||
const [selectedGroups, setSelectedGroups] = useState<string[]>(keyData.allowedGroups || []);
|
const [selectedGroups, setSelectedGroups] = useState<string[]>(keyData.allowedGroups || []);
|
||||||
@@ -105,6 +105,13 @@ const BearerKeyRow: React.FC<BearerKeyRowProps> = ({
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (accessType === 'custom' && selectedGroups.length === 0 && selectedServers.length === 0) {
|
||||||
|
showToast(
|
||||||
|
t('settings.selectAtLeastOneGroupOrServer') || 'Please select at least one group or server',
|
||||||
|
'error',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
try {
|
try {
|
||||||
@@ -135,6 +142,31 @@ const BearerKeyRow: React.FC<BearerKeyRowProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isGroupsMode = accessType === 'groups';
|
const isGroupsMode = accessType === 'groups';
|
||||||
|
const isCustomMode = accessType === 'custom';
|
||||||
|
|
||||||
|
// Helper function to format access type display text
|
||||||
|
const formatAccessTypeDisplay = (key: BearerKey): string => {
|
||||||
|
if (key.accessType === 'all') {
|
||||||
|
return t('settings.bearerKeyAccessAll') || 'All Resources';
|
||||||
|
}
|
||||||
|
if (key.accessType === 'groups') {
|
||||||
|
return `${t('settings.bearerKeyAccessGroups') || 'Groups'}: ${key.allowedGroups}`;
|
||||||
|
}
|
||||||
|
if (key.accessType === 'servers') {
|
||||||
|
return `${t('settings.bearerKeyAccessServers') || 'Servers'}: ${key.allowedServers}`;
|
||||||
|
}
|
||||||
|
if (key.accessType === 'custom') {
|
||||||
|
const parts: string[] = [];
|
||||||
|
if (key.allowedGroups && key.allowedGroups.length > 0) {
|
||||||
|
parts.push(`${t('settings.bearerKeyAccessGroups') || 'Groups'}: ${key.allowedGroups}`);
|
||||||
|
}
|
||||||
|
if (key.allowedServers && key.allowedServers.length > 0) {
|
||||||
|
parts.push(`${t('settings.bearerKeyAccessServers') || 'Servers'}: ${key.allowedServers}`);
|
||||||
|
}
|
||||||
|
return `${t('settings.bearerKeyAccessCustom') || 'Custom'}: ${parts.join('; ')}`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
return (
|
return (
|
||||||
@@ -194,7 +226,9 @@ const BearerKeyRow: React.FC<BearerKeyRowProps> = ({
|
|||||||
<select
|
<select
|
||||||
className="block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm form-select transition-shadow duration-200"
|
className="block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm form-select transition-shadow duration-200"
|
||||||
value={accessType}
|
value={accessType}
|
||||||
onChange={(e) => setAccessType(e.target.value as 'all' | 'groups' | 'servers')}
|
onChange={(e) =>
|
||||||
|
setAccessType(e.target.value as 'all' | 'groups' | 'servers' | 'custom')
|
||||||
|
}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
<option value="all">{t('settings.bearerKeyAccessAll') || 'All Resources'}</option>
|
<option value="all">{t('settings.bearerKeyAccessAll') || 'All Resources'}</option>
|
||||||
@@ -204,9 +238,14 @@ const BearerKeyRow: React.FC<BearerKeyRowProps> = ({
|
|||||||
<option value="servers">
|
<option value="servers">
|
||||||
{t('settings.bearerKeyAccessServers') || 'Specific Servers'}
|
{t('settings.bearerKeyAccessServers') || 'Specific Servers'}
|
||||||
</option>
|
</option>
|
||||||
|
<option value="custom">
|
||||||
|
{t('settings.bearerKeyAccessCustom') || 'Custom (Groups & Servers)'}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Show single selector for groups or servers mode */}
|
||||||
|
{!isCustomMode && (
|
||||||
<div className="flex-1 min-w-[200px]">
|
<div className="flex-1 min-w-[200px]">
|
||||||
<label
|
<label
|
||||||
className={`block text-sm font-medium mb-1 ${accessType === 'all' ? 'text-gray-400' : 'text-gray-700'}`}
|
className={`block text-sm font-medium mb-1 ${accessType === 'all' ? 'text-gray-400' : 'text-gray-700'}`}
|
||||||
@@ -227,6 +266,37 @@ const BearerKeyRow: React.FC<BearerKeyRowProps> = ({
|
|||||||
disabled={loading || accessType === 'all'}
|
disabled={loading || accessType === 'all'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Show both selectors for custom mode */}
|
||||||
|
{isCustomMode && (
|
||||||
|
<>
|
||||||
|
<div className="flex-1 min-w-[200px]">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
{t('settings.bearerKeyAllowedGroups') || 'Allowed groups'}
|
||||||
|
</label>
|
||||||
|
<MultiSelect
|
||||||
|
options={availableGroups}
|
||||||
|
selected={selectedGroups}
|
||||||
|
onChange={setSelectedGroups}
|
||||||
|
placeholder={t('settings.selectGroups') || 'Select groups...'}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-w-[200px]">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
{t('settings.bearerKeyAllowedServers') || 'Allowed servers'}
|
||||||
|
</label>
|
||||||
|
<MultiSelect
|
||||||
|
options={availableServers}
|
||||||
|
selected={selectedServers}
|
||||||
|
onChange={setSelectedServers}
|
||||||
|
placeholder={t('settings.selectServers') || 'Select servers...'}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end gap-2">
|
||||||
<button
|
<button
|
||||||
@@ -281,11 +351,7 @@ const BearerKeyRow: React.FC<BearerKeyRowProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
{keyData.accessType === 'all'
|
{formatAccessTypeDisplay(keyData)}
|
||||||
? t('settings.bearerKeyAccessAll') || 'All Resources'
|
|
||||||
: keyData.accessType === 'groups'
|
|
||||||
? `${t('settings.bearerKeyAccessGroups') || 'Groups'}: ${keyData.allowedGroups}`
|
|
||||||
: `${t('settings.bearerKeyAccessServers') || 'Servers'}: ${keyData.allowedServers}`}
|
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||||
<button
|
<button
|
||||||
@@ -737,7 +803,7 @@ const SettingsPage: React.FC = () => {
|
|||||||
name: string;
|
name: string;
|
||||||
token: string;
|
token: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
accessType: 'all' | 'groups' | 'servers';
|
accessType: 'all' | 'groups' | 'servers' | 'custom';
|
||||||
allowedGroups: string;
|
allowedGroups: string;
|
||||||
allowedServers: string;
|
allowedServers: string;
|
||||||
}>({
|
}>({
|
||||||
@@ -765,10 +831,10 @@ const SettingsPage: React.FC = () => {
|
|||||||
|
|
||||||
// Reset selected arrays when accessType changes
|
// Reset selected arrays when accessType changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (newBearerKey.accessType !== 'groups') {
|
if (newBearerKey.accessType !== 'groups' && newBearerKey.accessType !== 'custom') {
|
||||||
setNewSelectedGroups([]);
|
setNewSelectedGroups([]);
|
||||||
}
|
}
|
||||||
if (newBearerKey.accessType !== 'servers') {
|
if (newBearerKey.accessType !== 'servers' && newBearerKey.accessType !== 'custom') {
|
||||||
setNewSelectedServers([]);
|
setNewSelectedServers([]);
|
||||||
}
|
}
|
||||||
}, [newBearerKey.accessType]);
|
}, [newBearerKey.accessType]);
|
||||||
@@ -866,6 +932,17 @@ const SettingsPage: React.FC = () => {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
newBearerKey.accessType === 'custom' &&
|
||||||
|
newSelectedGroups.length === 0 &&
|
||||||
|
newSelectedServers.length === 0
|
||||||
|
) {
|
||||||
|
showToast(
|
||||||
|
t('settings.selectAtLeastOneGroupOrServer') || 'Please select at least one group or server',
|
||||||
|
'error',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await createBearerKey({
|
await createBearerKey({
|
||||||
name: newBearerKey.name,
|
name: newBearerKey.name,
|
||||||
@@ -873,11 +950,13 @@ const SettingsPage: React.FC = () => {
|
|||||||
enabled: newBearerKey.enabled,
|
enabled: newBearerKey.enabled,
|
||||||
accessType: newBearerKey.accessType,
|
accessType: newBearerKey.accessType,
|
||||||
allowedGroups:
|
allowedGroups:
|
||||||
newBearerKey.accessType === 'groups' && newSelectedGroups.length > 0
|
(newBearerKey.accessType === 'groups' || newBearerKey.accessType === 'custom') &&
|
||||||
|
newSelectedGroups.length > 0
|
||||||
? newSelectedGroups
|
? newSelectedGroups
|
||||||
: undefined,
|
: undefined,
|
||||||
allowedServers:
|
allowedServers:
|
||||||
newBearerKey.accessType === 'servers' && newSelectedServers.length > 0
|
(newBearerKey.accessType === 'servers' || newBearerKey.accessType === 'custom') &&
|
||||||
|
newSelectedServers.length > 0
|
||||||
? newSelectedServers
|
? newSelectedServers
|
||||||
: undefined,
|
: undefined,
|
||||||
} as any);
|
} as any);
|
||||||
@@ -901,7 +980,7 @@ const SettingsPage: React.FC = () => {
|
|||||||
name: string;
|
name: string;
|
||||||
token: string;
|
token: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
accessType: 'all' | 'groups' | 'servers';
|
accessType: 'all' | 'groups' | 'servers' | 'custom';
|
||||||
allowedGroups: string;
|
allowedGroups: string;
|
||||||
allowedServers: string;
|
allowedServers: string;
|
||||||
},
|
},
|
||||||
@@ -1128,7 +1207,7 @@ const SettingsPage: React.FC = () => {
|
|||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewBearerKey((prev) => ({
|
setNewBearerKey((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
accessType: e.target.value as 'all' | 'groups' | 'servers',
|
accessType: e.target.value as 'all' | 'groups' | 'servers' | 'custom',
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
@@ -1142,9 +1221,13 @@ const SettingsPage: React.FC = () => {
|
|||||||
<option value="servers">
|
<option value="servers">
|
||||||
{t('settings.bearerKeyAccessServers') || 'Specific Servers'}
|
{t('settings.bearerKeyAccessServers') || 'Specific Servers'}
|
||||||
</option>
|
</option>
|
||||||
|
<option value="custom">
|
||||||
|
{t('settings.bearerKeyAccessCustom') || 'Custom (Groups & Servers)'}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{newBearerKey.accessType !== 'custom' && (
|
||||||
<div className="flex-1 min-w-[200px]">
|
<div className="flex-1 min-w-[200px]">
|
||||||
<label
|
<label
|
||||||
className={`block text-sm font-medium mb-1 ${newBearerKey.accessType === 'all' ? 'text-gray-400' : 'text-gray-700'}`}
|
className={`block text-sm font-medium mb-1 ${newBearerKey.accessType === 'all' ? 'text-gray-400' : 'text-gray-700'}`}
|
||||||
@@ -1177,6 +1260,36 @@ const SettingsPage: React.FC = () => {
|
|||||||
disabled={loading || newBearerKey.accessType === 'all'}
|
disabled={loading || newBearerKey.accessType === 'all'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{newBearerKey.accessType === 'custom' && (
|
||||||
|
<>
|
||||||
|
<div className="flex-1 min-w-[200px]">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
{t('settings.bearerKeyAllowedGroups') || 'Allowed groups'}
|
||||||
|
</label>
|
||||||
|
<MultiSelect
|
||||||
|
options={availableGroups}
|
||||||
|
selected={newSelectedGroups}
|
||||||
|
onChange={setNewSelectedGroups}
|
||||||
|
placeholder={t('settings.selectGroups') || 'Select groups...'}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-w-[200px]">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
{t('settings.bearerKeyAllowedServers') || 'Allowed servers'}
|
||||||
|
</label>
|
||||||
|
<MultiSelect
|
||||||
|
options={availableServers}
|
||||||
|
selected={newSelectedServers}
|
||||||
|
onChange={setNewSelectedServers}
|
||||||
|
placeholder={t('settings.selectServers') || 'Select servers...'}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end gap-2">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ export interface ApiResponse<T = any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bearer authentication key configuration (frontend view model)
|
// Bearer authentication key configuration (frontend view model)
|
||||||
export type BearerKeyAccessType = 'all' | 'groups' | 'servers';
|
export type BearerKeyAccessType = 'all' | 'groups' | 'servers' | 'custom';
|
||||||
|
|
||||||
export interface BearerKey {
|
export interface BearerKey {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -568,6 +568,7 @@
|
|||||||
"bearerKeyAccessAll": "All",
|
"bearerKeyAccessAll": "All",
|
||||||
"bearerKeyAccessGroups": "Groups",
|
"bearerKeyAccessGroups": "Groups",
|
||||||
"bearerKeyAccessServers": "Servers",
|
"bearerKeyAccessServers": "Servers",
|
||||||
|
"bearerKeyAccessCustom": "Custom",
|
||||||
"bearerKeyAllowedGroups": "Allowed groups",
|
"bearerKeyAllowedGroups": "Allowed groups",
|
||||||
"bearerKeyAllowedServers": "Allowed servers",
|
"bearerKeyAllowedServers": "Allowed servers",
|
||||||
"addBearerKey": "Add key",
|
"addBearerKey": "Add key",
|
||||||
|
|||||||
@@ -569,6 +569,7 @@
|
|||||||
"bearerKeyAccessAll": "Toutes",
|
"bearerKeyAccessAll": "Toutes",
|
||||||
"bearerKeyAccessGroups": "Groupes",
|
"bearerKeyAccessGroups": "Groupes",
|
||||||
"bearerKeyAccessServers": "Serveurs",
|
"bearerKeyAccessServers": "Serveurs",
|
||||||
|
"bearerKeyAccessCustom": "Personnalisée",
|
||||||
"bearerKeyAllowedGroups": "Groupes autorisés",
|
"bearerKeyAllowedGroups": "Groupes autorisés",
|
||||||
"bearerKeyAllowedServers": "Serveurs autorisés",
|
"bearerKeyAllowedServers": "Serveurs autorisés",
|
||||||
"addBearerKey": "Ajouter une clé",
|
"addBearerKey": "Ajouter une clé",
|
||||||
|
|||||||
@@ -569,6 +569,7 @@
|
|||||||
"bearerKeyAccessAll": "Tümü",
|
"bearerKeyAccessAll": "Tümü",
|
||||||
"bearerKeyAccessGroups": "Gruplar",
|
"bearerKeyAccessGroups": "Gruplar",
|
||||||
"bearerKeyAccessServers": "Sunucular",
|
"bearerKeyAccessServers": "Sunucular",
|
||||||
|
"bearerKeyAccessCustom": "Özel",
|
||||||
"bearerKeyAllowedGroups": "İzin verilen gruplar",
|
"bearerKeyAllowedGroups": "İzin verilen gruplar",
|
||||||
"bearerKeyAllowedServers": "İzin verilen sunucular",
|
"bearerKeyAllowedServers": "İzin verilen sunucular",
|
||||||
"addBearerKey": "Anahtar ekle",
|
"addBearerKey": "Anahtar ekle",
|
||||||
|
|||||||
@@ -570,6 +570,7 @@
|
|||||||
"bearerKeyAccessAll": "全部",
|
"bearerKeyAccessAll": "全部",
|
||||||
"bearerKeyAccessGroups": "指定分组",
|
"bearerKeyAccessGroups": "指定分组",
|
||||||
"bearerKeyAccessServers": "指定服务器",
|
"bearerKeyAccessServers": "指定服务器",
|
||||||
|
"bearerKeyAccessCustom": "自定义",
|
||||||
"bearerKeyAllowedGroups": "允许访问的分组",
|
"bearerKeyAllowedGroups": "允许访问的分组",
|
||||||
"bearerKeyAllowedServers": "允许访问的服务器",
|
"bearerKeyAllowedServers": "允许访问的服务器",
|
||||||
"addBearerKey": "新增密钥",
|
"addBearerKey": "新增密钥",
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export const createBearerKey = async (req: Request, res: Response): Promise<void
|
|||||||
return;
|
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' });
|
res.status(400).json({ success: false, message: 'Invalid accessType' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ export const updateBearerKey = async (req: Request, res: Response): Promise<void
|
|||||||
if (token !== undefined) updates.token = token;
|
if (token !== undefined) updates.token = token;
|
||||||
if (enabled !== undefined) updates.enabled = enabled;
|
if (enabled !== undefined) updates.enabled = enabled;
|
||||||
if (accessType !== undefined) {
|
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' });
|
res.status(400).json({ success: false, message: 'Invalid accessType' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class BearerKey {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
|
||||||
@Column({ type: 'varchar', length: 20, default: 'all' })
|
@Column({ type: 'varchar', length: 20, default: 'all' })
|
||||||
accessType: 'all' | 'groups' | 'servers';
|
accessType: 'all' | 'groups' | 'servers' | 'custom';
|
||||||
|
|
||||||
@Column({ type: 'simple-json', nullable: true })
|
@Column({ type: 'simple-json', nullable: true })
|
||||||
allowedGroups?: string[];
|
allowedGroups?: string[];
|
||||||
|
|||||||
@@ -88,6 +88,29 @@ const isBearerKeyAllowedForRequest = async (req: Request, key: BearerKey): Promi
|
|||||||
return groupServerNames.some((name) => allowedServers.includes(name));
|
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
|
// Unknown accessType with matched group
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -102,8 +125,8 @@ const isBearerKeyAllowedForRequest = async (req: Request, key: BearerKey): Promi
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.accessType === 'servers') {
|
if (key.accessType === 'servers' || key.accessType === 'custom') {
|
||||||
// For server-scoped keys, check if the server is in allowedServers
|
// For server-scoped or custom-scoped keys, check if the server is in allowedServers
|
||||||
const allowedServers = key.allowedServers || [];
|
const allowedServers = key.allowedServers || [];
|
||||||
return allowedServers.includes(matchedServer.name);
|
return allowedServers.includes(matchedServer.name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ export interface OAuthServerConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bearer authentication key configuration
|
// Bearer authentication key configuration
|
||||||
export type BearerKeyAccessType = 'all' | 'groups' | 'servers';
|
export type BearerKeyAccessType = 'all' | 'groups' | 'servers' | 'custom';
|
||||||
|
|
||||||
export interface BearerKey {
|
export interface BearerKey {
|
||||||
id: string; // Unique identifier for the key
|
id: string; // Unique identifier for the key
|
||||||
@@ -252,8 +252,8 @@ export interface BearerKey {
|
|||||||
token: string; // Bearer token value
|
token: string; // Bearer token value
|
||||||
enabled: boolean; // Whether this key is enabled
|
enabled: boolean; // Whether this key is enabled
|
||||||
accessType: BearerKeyAccessType; // Access scope type
|
accessType: BearerKeyAccessType; // Access scope type
|
||||||
allowedGroups?: string[]; // Allowed group names when accessType === 'groups'
|
allowedGroups?: string[]; // Allowed group names when accessType === 'groups' or 'custom'
|
||||||
allowedServers?: string[]; // Allowed server names when accessType === 'servers'
|
allowedServers?: string[]; // Allowed server names when accessType === 'servers' or 'custom'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Represents the settings for MCP servers
|
// Represents the settings for MCP servers
|
||||||
|
|||||||
Reference in New Issue
Block a user