أدلة هندسية
استيراد البيانات في Supabase وRLS والحقول التابعة للمستأجر
افصل قيم CSV عن صلاحية تحديد المستأجر. تعرّف على رموز JWT للمستخدمين، والوصول عبر دور الخدمة، وقيد COPY في PostgreSQL عندما ينطبق RLS.
حدّد مستأجر عملية الاستيراد من خلال قرار يتخذه التطبيق بعد التحقق من الهوية والصلاحية. استبعده من خريطة الأعمدة التي يحددها العميل. ثم افرض الكتابة المسموح بها في الوجهة بالدور الذي سينفذها فعليًا.
تمكين RLS ليس إلا جزءًا من هذا التصميم. لا تتساوى صلاحيات طلب إلى API في Supabase يحمل رمز JWT لمستخدم، وطلب من الخادم يستخدم صلاحيات دور الخدمة، وجلسة psql لمالك قاعدة البيانات. نجاح الاستيراد في لوحة التحكم لا يثبت أنه سينجح عبر جلسة العميل، ولا أن عزل المستأجرين قد اختُبر.
إذا كنت تحتاج إلى خطوات رفع الملف فقط، فابدأ بدليل استيراد CSV إلى Supabase. تتناول هذه الصفحة حدود الصلاحيات وراء عمليات الكتابة تلك.
النطاق والافتراضات. سلوك PostgreSQL 17 وSupabase Auth وData API. أمثلة السياسات توضيحية وتتطلب نموذج العضوية ومنح الامتيازات المذكور.
حدّد مسار التنفيذ أولًا
| المسار | الصلاحيات التي ينبغي فحصها | ما الذي تثبته عملية كتابة ناجحة |
|---|---|---|
| Data API مع رمز JWT لمستخدم سجّل الدخول | الدور الفعلي للمستخدم المصادَق عليه، ومطالبات JWT، والامتيازات الممنوحة، والسياسات | اجتياز الطلب الفحوص المنطبقة في ذلك الوقت |
| خادم خلفي يستخدم تخويل دور الخدمة | الدور ذو الامتيازات والتخويل الذي يفرضه الخادم بنفسه | لا تستنتج فرض RLS؛ فالدور service_role يمتلك BYPASSRLS |
| لوحة التحكم / اتصال بقاعدة البيانات ذي امتيازات | دور الجلسة الفعلي، والملكية، وسمات التجاوز | ليس دليلًا على وصول مستخدم تطبيق عادي |
| حساب دخول إلى قاعدة البيانات محدود النطاق ويتحكم فيه العميل | الامتيازات الممنوحة للدور، وملكية الجدول، وسياسات RLS المنطبقة، وسياق الجلسة | الصلاحيات والفحوص التي استُخدمت عبر ذلك الحساب فقط |
يميّز دليل مفاتيح API في Supabase بين الوصول بالمفتاح القابل للنشر أو anon، والوصول بالمفتاح السري أو مفتاح دور الخدمة. ويشرح دليل RLS أيضًا تفصيلًا مهمًا يخص العميل البرمجي: يمكن لعميل هُيّئ بمفتاح خدمة أن يتبع سياق RLS لمستخدم سجّل الدخول عندما يُستبدل تخويله بجلسة ذلك المستخدم. افحص بيانات اعتماد Authorization الفعلية، ولا تكتفِ بالمفتاح المستخدم عند إنشاء العميل.
في PostgreSQL، يتجاوز المستخدمون الفائقون والأدوار ذات BYPASSRLS أمان الصفوف. ويتجاوزه مالكو الجداول عادةً أيضًا، ما لم يُطبّق FORCE ROW LEVEL SECURITY على المالك. لا يقيّد FORCE مستخدمًا فائقًا أو دورًا ذا BYPASSRLS. لذلك لا يكفي الاختبار بصفة مالك الجدول فقط.
تعالج الامتيازات الممنوحة وRLS مشكلتين مختلفتين. يحتاج الدور إلى الامتياز المناسب على الجدول أو العمود؛ ويجب أن تسمح سياسة الصفوف أيضًا بالعملية حين ينطبق RLS. إذا لم تسمح أي سياسة منطبقة بالوصول، فإن الجدول المفعّل عليه RLS يرفض وصول الأدوار العادية افتراضيًا. راجع قواعد أمان الصفوف في PostgreSQL.
اربط كل عملية استيراد بمستأجر مخوّل
تصوّر مؤسسًا يستورد جهات اتصال لمساحة عمل. يحتوي ملف CSV لديه على account_id من نظام CRM سابق. وتحتوي قاعدة بياناتك أيضًا على معرّف حساب. قد تفيد القيمة القديمة بوصفها مرجعًا خارجيًا، لكنها ليست دليلًا على أن منفذ الاستيراد مخوّل بالكتابة إلى مساحة العمل المقابلة في نظامك.
ينبغي أن يتحقق التطبيق من هوية المشغّل، ويفحص صلاحية الاستيراد لديه لمساحة العمل المختارة، ويربط مساحة العمل تلك بمهمة الاستيراد. عندئذ لا تتضمن خريطة الصفوف المعتمدة إلا حقولًا مثل البريد الإلكتروني واسم العرض. ويتولى مكوّن الكتابة توفير سياق المستأجر بصورة مستقلة.
subject = authenticate(request)
tenant = authorizeImport(subject, selectedWorkspace)
job = createJob({ tenant, actor: subject.id, contractVersion })
for each submitted row:
reject keys outside the approved source fields
validate source values again on the server
write approved values with tenant = job.tenantالتحقق من الهوية وحده لا يمنح التخويل. قد يكون المستخدم مسجّل الدخول دون أن ينتمي إلى مساحة العمل المختارة، أو قد ينتمي إليها دون صلاحية الاستيراد. تحقق من صلاحية هذه العملية، وحدّد أثر سحب الصلاحيات على العمل المنتظر في الطابور. يجب ألا تكتسب مهمة طويلة الأمد صلاحية مفتوحة المدة بصمت من جلسة كانت صالحة بالأمس.
لا تستخدم بيانات وصفية يستطيع المستخدم تعديلها مصدرًا لصلاحية تحديد المستأجر. إذا استخدمت مطالبات JWT، فحدّد من يصدرها، ومن يستطيع تغييرها، وكيف تُعالَج بيانات العضوية التي لم تعد محدّثة. لا تتجاوز فائدة المطالبة الموقّعة موثوقية العملية التي أنشأتها.
استخدم WITH CHECK لتقييد الصفوف الجديدة
توضح السياسة التالية تطبيقًا يسجّل فيه جدول عضويات موثوق من يحق له الاستيراد إلى كل مستأجر. افترض وجود public.contacts(tenant_id uuid, email text) مع قيد NOT NULL على العمودين. وافترض أن public.tenant_memberships(tenant_id uuid, user_id uuid, can_import boolean) محمي من إنشاء المستخدمين منحًا للصلاحيات بأنفسهم، وأن صلاحيات SELECT وسياساته تتيح للمستخدم قراءة عضوياته الخاصة.
ALTER TABLE public.contacts ENABLE ROW LEVEL SECURITY;
CREATE POLICY contacts_member_import
ON public.contacts
FOR INSERT TO authenticated
WITH CHECK (
EXISTS (
SELECT 1
FROM public.tenant_memberships AS membership
WHERE membership.tenant_id = contacts.tenant_id
AND membership.user_id = (SELECT auth.uid())
AND membership.can_import
)
);هذا جزء من سياسة، وليس إعدادًا كاملًا. يحتاج الدور أيضًا إلى امتيازات INSERT مناسبة. إذا كان الطلب يعيد صفوفًا، فراجع امتيازات SELECT وسياساته التي تتطلبها تلك الاستجابة. تستخدم سياسة INSERT عبارة WITH CHECK لفحص الصف المقترح؛ ولا تحل سياسة SELECT محلها.
راجع كل سياسة موجودة. تتحد السياسات السماحية عمومًا باستخدام OR، لذا قد لا تؤدي إضافة هذه السياسة بجانب سياسة INSERT سماحية واسعة إلى تضييق الوصول. تتحد السياسات التقييدية بطريقة مختلفة وتحتاج إلى سياسة سماحية منطبقة. قيّم مجموعة السياسات كاملة بدلًا من اعتبار تعبير واحد يبدو سليمًا ممثلًا لكل حدود الصلاحيات.
يسمح فحص العضوية هذا بمستأجر مخوّل؛ لكنه لا يربط الطلب بمستأجر مهمة واحدة سبق اختياره. يظل على مكوّن الكتابة رفض حقول المستأجر التي يتحكم فيها الملف واستخدام القيمة المخوّلة للمهمة. وإذا كان نموذج التهديد يشمل تجاوز مكوّن الكتابة، فصمّم أيضًا حدودًا للمهمة تفرضها الوجهة. لا تعرض هذا الجزء من السياسة على أنه يوفر ذلك الضمان الأقوى.
COPY ليس طريقًا مختصرًا للاستيراد وفق RLS الخاص بالمستخدم
لا يدعم PostgreSQL 17 استخدام COPY FROM عندما ينطبق RLS على الدور المنفذ. ويوصي التوثيق باستخدام عبارات INSERT مكافئة. قد يكون تحميل جدول تجهيز مؤقت ثم استخدام INSERT مخوّل إلى الجدول المستهدف تصميمًا مناسبًا، بشرط مراجعة الجلسة، وقيم الصفوف، والامتيازات الممنوحة، والسياسات.
قد يختلف سلوك مالك الجدول أو دور يملك صلاحية التجاوز، لأن أمان الصفوف قد لا ينطبق عليه. هذا ليس حلًا بديلًا يحافظ على ضمان سياسة المستخدم الأصلي. بل هو مسار ترحيل ذي امتيازات، يجب إرساء فحوص المستأجر والمراجعة التشغيلية الخاصة به بصورة منفصلة.
كذلك لا تحمل اتصالات SQL المباشرة تلقائيًا سياق JWT لمستخدم Supabase. تعتمد دالة مثل auth.uid() على توفر مطالبات الطلب أو الجلسة عبر المسار المعني. لا تفترض أن حساب دخول psql يمثل المستخدم النهائي لمجرد اتصاله بقاعدة البيانات نفسها.
في ترحيل لمرة واحدة ينفذه المشتري، قد يكون التنفيذ بامتيازات مرتفعة خيارًا مقصودًا. اطلب من مهندس المشتري مراجعة إسناد المصدر إلى المستأجر، والأعمدة المحددة، والجدول المستهدف، والصفوف الناتجة. ميّز هذا الإجراء عن أداة استيراد للمستخدم النهائي تفرض RLS. يصف توثيق COPY هذا القيد في قاعدة البيانات.
اختبر عمليات الكتابة المحظورة، ولا تكتفِ بالعملية الناجحة
استخدم مستأجرين معزولين للاختبار وصفوفًا اصطناعية. نفّذ الاختبارات بالدور ومسار الطلب نفسيهما اللذين تستخدمهما أداة الاستيراد الفعلية، وبالامتيازات الممنوحة ومجموعة السياسات الكاملة نفسيهما.
- يستطيع مستخدم لديه صلاحية استيراد للمستأجر A إنشاء صف معتمد داخل A.
- يُرفض طلب مستخدم للمستأجر B إذا كانت صلاحية الاستيراد لديه مقتصرة على المستأجر A.
- في مهمة مرتبطة بالفعل بالمستأجر A، تُرفض قيمة المستأجر B المقدمة حتى إن كان للمستخدم أيضًا صلاحية مستقلة على B. يتطلب ذلك فرض القيود على مستوى المهمة إضافةً إلى سياسة العضوية.
- يُرفض العضو الذي لا يملك صلاحية الاستيراد.
- لا تحصل جلسة غير مصادَق عليها أو منتهية الصلاحية على مسار احتياطي للكتابة بامتيازات مرتفعة.
- يُرفض حقل المستأجر المقدّم حتى إن صادف أن قيمته تساوي المستأجر الصحيح.
- تتبع المهمة المنتظرة في الطابور القاعدة الموثّقة بعد سحب العضوية.
بعد النجاح، اقرأ الصف عبر مسار التطبيق المعتاد للمستأجرين A وB. تحقق من إظهاره لمن يحق له رؤيته، إضافةً إلى التحقق من إدراجه. امنع الأخطاء من كشف معرّفات مستأجر آخر أو بياناته. إذا نجح الاختبار بصفة المالك فقط، فقد اختبرت صلاحيات المالك، لا المسار المقصود للعميل.
يستحق المفتاح الأجنبي من جهة اتصال إلى شركة مراجعة أيضًا: فقد يكون معرّف شركة صالحًا على مستوى النظام كله لكنه تابع لمستأجر آخر. استخدم قيدًا مرجعيًا مناسبًا محصورًا بنطاق المستأجر، واستراتيجية لحلّ المراجع. يشرح دليل المفاتيح الأجنبية هذا المثال بالتفصيل.
علاقة ذلك بـImportFlow
يفصل ImportFlow تخويل التطبيق عن قيم المستأجر المقدمة في الجداول. راجع البنية الحالية وحدود التنفيذ لمعرفة توزيع المسؤوليات على مستوى المنتج.
يظل التطبيق مسؤولًا عن صلاحيات المستخدم والمستأجر. يتعامل ImportFlow مع قيم المستأجر أو النظام المحمية بوصفها سياقًا يقدمه التطبيق، لا حقولًا في الجداول يستطيع العميل تعديلها، ولا يكفي RLS وحده لإثبات تلك الصلاحيات.