feat(mcp): granular OAuth scopes and per-client rate limiting

- Split `media:read` into `geo:read` and `weather:read` scopes
- Add dedicated `atlas:read/write` scopes (previously under `places`)
- Add dedicated `todos:read/write` scopes (previously under `collab`)
- Rate limiting now keyed by userId+clientId instead of userId alone
- Bind MCP sessions to the OAuth client that created them
- Log MCP tool calls to audit log with clientId
- Invalidate all MCP sessions on addon state change
- Reduce session sweep interval from 10min to 1min
- Update all translations with new scope labels
This commit is contained in:
jubnl
2026-04-11 02:06:09 +02:00
parent 4670d4914c
commit 535c06bb3f
39 changed files with 1930 additions and 237 deletions
+111 -6
View File
@@ -184,9 +184,6 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'admin.notifications.none': 'معطّل',
'admin.notifications.email': 'البريد الإلكتروني (SMTP)',
'admin.notifications.webhook': 'Webhook',
'admin.notifications.events': 'أحداث الإشعارات',
'admin.notifications.eventsHint': 'اختر الأحداث التي تُفعّل الإشعارات لجميع المستخدمين.',
'admin.notifications.configureFirst': 'قم بتكوين إعدادات SMTP أو Webhook أدناه أولاً، ثم قم بتفعيل الأحداث.',
'admin.notifications.save': 'حفظ إعدادات الإشعارات',
'admin.notifications.saved': 'تم حفظ إعدادات الإشعارات',
'admin.notifications.testWebhook': 'إرسال webhook تجريبي',
@@ -233,7 +230,7 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'settings.mcp.endpoint': 'نقطة نهاية MCP',
'settings.mcp.clientConfig': 'إعداد العميل',
'settings.mcp.clientConfigHint': 'استبدل <your_token> برمز API من القائمة أدناه. قد يحتاج مسار npx إلى ضبط وفق نظامك (مثلاً C:\\PROGRA~1\\nodejs\\npx.cmd على Windows).',
'settings.mcp.clientConfigHintOAuth': 'Replace <your_client_id> and <your_client_secret> with the credentials shown in the OAuth 2.1 client you created above. mcp-remote will open your browser to complete the authorization the first time you connect. The path to npx may need to be adjusted for your system (e.g. C:\PROGRA~1\nodejs\npx.cmd on Windows).',
'settings.mcp.clientConfigHintOAuth': 'استبدل <your_client_id> و<your_client_secret> ببيانات الاعتماد المعروضة في عميل OAuth 2.1 الذي أنشأته أعلاه. سيفتح mcp-remote متصفحك لإتمام التفويض في أول اتصال. قد يحتاج مسار npx إلى تعديل حسب نظامك (مثال: C:\PROGRA~1\nodejs\npx.cmd على Windows).',
'settings.mcp.copy': 'نسخ',
'settings.mcp.copied': 'تم النسخ!',
'settings.mcp.apiTokens': 'رموز API',
@@ -255,6 +252,48 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'settings.mcp.toast.createError': 'فشل إنشاء الرمز',
'settings.mcp.toast.deleted': 'تم حذف الرمز',
'settings.mcp.toast.deleteError': 'فشل حذف الرمز',
'settings.mcp.apiTokensDeprecated': 'رموز API قديمة وستُزال في إصدار مستقبلي. يُرجى استخدام عملاء OAuth 2.1 بدلاً منها.',
'settings.oauth.clients': 'عملاء OAuth 2.1',
'settings.oauth.clientsHint': 'سجّل عملاء OAuth 2.1 للسماح لتطبيقات MCP الخارجية (Claude Web وCursor وغيرها) بالاتصال دون رموز ثابتة.',
'settings.oauth.createClient': 'عميل جديد',
'settings.oauth.noClients': 'لا يوجد عملاء OAuth مسجلون.',
'settings.oauth.clientId': 'معرّف العميل',
'settings.oauth.clientSecret': 'سر العميل',
'settings.oauth.deleteClient': 'حذف العميل',
'settings.oauth.deleteClientMessage': 'سيتم حذف هذا العميل وجميع الجلسات النشطة بشكل دائم. ستفقد أي تطبيق يستخدمه وصوله فوراً.',
'settings.oauth.rotateSecret': 'تجديد السر',
'settings.oauth.rotateSecretMessage': 'سيتم إنشاء سر عميل جديد وإبطال جميع الجلسات الحالية فوراً. حدّث تطبيقك قبل إغلاق هذا الحوار.',
'settings.oauth.rotateSecretConfirm': 'تجديد',
'settings.oauth.rotateSecretConfirming': 'جارٍ التجديد…',
'settings.oauth.rotateSecretDoneTitle': 'تم إنشاء سر جديد',
'settings.oauth.rotateSecretDoneWarning': 'يُعرض هذا السر مرة واحدة فقط. انسخه الآن وحدّث تطبيقك — تم إبطال جميع الجلسات السابقة.',
'settings.oauth.activeSessions': 'جلسات OAuth النشطة',
'settings.oauth.sessionScopes': 'النطاقات',
'settings.oauth.sessionExpires': 'تنتهي',
'settings.oauth.revoke': 'إلغاء',
'settings.oauth.revokeSession': 'إلغاء الجلسة',
'settings.oauth.revokeSessionMessage': 'سيؤدي هذا إلى إلغاء الوصول لهذه الجلسة OAuth فوراً.',
'settings.oauth.modal.createTitle': 'تسجيل عميل OAuth',
'settings.oauth.modal.presets': 'إعدادات سريعة',
'settings.oauth.modal.clientName': 'اسم التطبيق',
'settings.oauth.modal.clientNamePlaceholder': 'مثال: Claude Web، تطبيق MCP الخاص بي',
'settings.oauth.modal.redirectUris': 'عناوين URI لإعادة التوجيه',
'settings.oauth.modal.redirectUrisPlaceholder': 'https://your-app.com/callback\nhttps://your-app.com/auth',
'settings.oauth.modal.redirectUrisHint': 'عنوان URI واحد لكل سطر. يُطلب HTTPS (localhost مستثنى). يُطبق تطابق دقيق.',
'settings.oauth.modal.scopes': 'النطاقات المسموح بها',
'settings.oauth.modal.scopesHint': 'list_trips وget_trip_summary متاحان دائماً — لا يُطلب نطاق. يساعدان الذكاء الاصطناعي في اكتشاف معرّفات الرحلات.',
'settings.oauth.modal.selectAll': 'تحديد الكل',
'settings.oauth.modal.deselectAll': 'إلغاء تحديد الكل',
'settings.oauth.modal.creating': 'جارٍ التسجيل…',
'settings.oauth.modal.create': 'تسجيل العميل',
'settings.oauth.modal.createdTitle': 'تم تسجيل العميل',
'settings.oauth.modal.createdWarning': 'يُعرض سر العميل مرة واحدة فقط. انسخه الآن — لا يمكن استرداده.',
'settings.oauth.toast.createError': 'فشل تسجيل عميل OAuth',
'settings.oauth.toast.deleted': 'تم حذف عميل OAuth',
'settings.oauth.toast.deleteError': 'فشل حذف عميل OAuth',
'settings.oauth.toast.revoked': 'تم إلغاء الجلسة',
'settings.oauth.toast.revokeError': 'فشل إلغاء الجلسة',
'settings.oauth.toast.rotateError': 'فشل تجديد سر العميل',
'settings.account': 'الحساب',
'settings.about': 'حول',
'settings.about.reportBug': 'الإبلاغ عن خطأ',
@@ -1022,6 +1061,7 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'budget.totalBudget': 'إجمالي الميزانية',
'budget.byCategory': 'حسب الفئة',
'budget.editTooltip': 'انقر للتعديل',
'budget.linkedToReservation': 'مرتبط بحجز — عدّل الاسم هناك',
'budget.confirm.deleteCategory': 'هل تريد حذف الفئة "{name}" مع {count} إدخالات؟',
'budget.deleteCategory': 'حذف الفئة',
'budget.perPerson': 'لكل شخص',
@@ -1122,6 +1162,9 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'packing.template': 'قالب',
'packing.templateApplied': 'تمت إضافة {count} عنصر من القالب',
'packing.templateError': 'فشل تطبيق القالب',
'packing.saveAsTemplate': 'حفظ كقالب',
'packing.templateName': 'اسم القالب',
'packing.templateSaved': 'تم حفظ قائمة الحقائب كقالب',
'packing.bags': 'أمتعة',
'packing.noBag': 'غير معيّن',
'packing.totalWeight': 'الوزن الإجمالي',
@@ -1405,8 +1448,6 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'memories.reviewTitle': 'مراجعة صورك',
'memories.reviewHint': 'انقر على الصور لاستبعادها من المشاركة.',
'memories.shareCount': 'مشاركة {count} صور',
'memories.immichUrl': 'عنوان خادم Immich',
'memories.immichApiKey': 'مفتاح API',
'memories.testConnection': 'اختبار الاتصال',
'memories.testFirst': 'اختبر الاتصال أولاً',
'memories.connected': 'متصل',
@@ -1703,6 +1744,70 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'notif.generic.text': 'لديك إشعار جديد',
'notif.dev.unknown_event.title': '[DEV] حدث غير معروف',
'notif.dev.unknown_event.text': 'نوع الحدث "{event}" غير مسجل في EVENT_NOTIFICATION_CONFIG',
// OAuth scope groups
'oauth.scope.group.trips': 'الرحلات',
'oauth.scope.group.places': 'الأماكن',
'oauth.scope.group.atlas': 'Atlas',
'oauth.scope.group.packing': 'الأمتعة',
'oauth.scope.group.todos': 'المهام',
'oauth.scope.group.budget': 'الميزانية',
'oauth.scope.group.reservations': 'الحجوزات',
'oauth.scope.group.collab': 'التعاون',
'oauth.scope.group.notifications': 'الإشعارات',
'oauth.scope.group.vacay': 'الإجازة',
'oauth.scope.group.geo': 'Geo',
'oauth.scope.group.weather': 'الطقس',
// OAuth scope labels & descriptions
'oauth.scope.trips:read.label': 'عرض الرحلات وخطط السفر',
'oauth.scope.trips:read.description': 'قراءة الرحلات والأيام والملاحظات والأعضاء',
'oauth.scope.trips:write.label': 'تحرير الرحلات وخطط السفر',
'oauth.scope.trips:write.description': 'إنشاء وتحديث الرحلات والأيام والملاحظات وإدارة الأعضاء',
'oauth.scope.trips:delete.label': 'حذف الرحلات',
'oauth.scope.trips:delete.description': 'حذف الرحلات بأكملها نهائياً — هذا الإجراء لا يمكن التراجع عنه',
'oauth.scope.trips:share.label': 'إدارة روابط المشاركة',
'oauth.scope.trips:share.description': 'إنشاء روابط مشاركة عامة وتحديثها وإلغاؤها',
'oauth.scope.places:read.label': 'عرض الأماكن وبيانات الخريطة',
'oauth.scope.places:read.description': 'قراءة الأماكن وتعيينات الأيام والعلامات والفئات',
'oauth.scope.places:write.label': 'إدارة الأماكن',
'oauth.scope.places:write.description': 'إنشاء وتحديث وحذف الأماكن والتعيينات والعلامات',
'oauth.scope.atlas:read.label': 'عرض Atlas',
'oauth.scope.atlas:read.description': 'قراءة الدول والمناطق المزارة وقائمة الأمنيات',
'oauth.scope.atlas:write.label': 'إدارة Atlas',
'oauth.scope.atlas:write.description': 'تعليم الدول والمناطق كمزارة، وإدارة قائمة الأمنيات',
'oauth.scope.packing:read.label': 'عرض قوائم الأمتعة',
'oauth.scope.packing:read.description': 'قراءة عناصر الأمتعة والحقائب ومُسنَدي الفئات',
'oauth.scope.packing:write.label': 'إدارة قوائم الأمتعة',
'oauth.scope.packing:write.description': 'إضافة وتحديث وحذف وتبديل وإعادة ترتيب عناصر الأمتعة والحقائب',
'oauth.scope.todos:read.label': 'عرض قوائم المهام',
'oauth.scope.todos:read.description': 'قراءة مهام الرحلة ومُسنَدي الفئات',
'oauth.scope.todos:write.label': 'إدارة قوائم المهام',
'oauth.scope.todos:write.description': 'إنشاء وتحديث وتبديل وحذف وإعادة ترتيب المهام',
'oauth.scope.budget:read.label': 'عرض الميزانية',
'oauth.scope.budget:read.description': 'قراءة بنود الميزانية وتفاصيل النفقات',
'oauth.scope.budget:write.label': 'إدارة الميزانية',
'oauth.scope.budget:write.description': 'إنشاء وتحديث وحذف بنود الميزانية',
'oauth.scope.reservations:read.label': 'عرض الحجوزات',
'oauth.scope.reservations:read.description': 'قراءة الحجوزات وتفاصيل الإقامة',
'oauth.scope.reservations:write.label': 'إدارة الحجوزات',
'oauth.scope.reservations:write.description': 'إنشاء وتحديث وحذف وإعادة ترتيب الحجوزات',
'oauth.scope.collab:read.label': 'عرض التعاون',
'oauth.scope.collab:read.description': 'قراءة ملاحظات التعاون والاستطلاعات والرسائل',
'oauth.scope.collab:write.label': 'إدارة التعاون',
'oauth.scope.collab:write.description': 'إنشاء وتحديث وحذف الملاحظات والاستطلاعات والرسائل التعاونية',
'oauth.scope.notifications:read.label': 'عرض الإشعارات',
'oauth.scope.notifications:read.description': 'قراءة إشعارات التطبيق وأعداد غير المقروءة',
'oauth.scope.notifications:write.label': 'إدارة الإشعارات',
'oauth.scope.notifications:write.description': 'تعليم الإشعارات كمقروءة والرد عليها',
'oauth.scope.vacay:read.label': 'عرض خطط الإجازة',
'oauth.scope.vacay:read.description': 'قراءة بيانات تخطيط الإجازة والإدخالات والإحصاءات',
'oauth.scope.vacay:write.label': 'إدارة خطط الإجازة',
'oauth.scope.vacay:write.description': 'إنشاء وإدارة إدخالات الإجازة والعطلات وخطط الفريق',
'oauth.scope.geo:read.label': 'الخرائط والترميز الجغرافي',
'oauth.scope.geo:read.description': 'البحث عن مواقع وحل عناوين الخرائط والترميز الجغرافي العكسي للإحداثيات',
'oauth.scope.weather:read.label': 'توقعات الطقس',
'oauth.scope.weather:read.description': 'جلب توقعات الطقس لمواقع الرحلة وتواريخها',
}
export default ar