تنفيذ نموذج الوصول المستند إلى الدور باستخدام أمان مستوى الصف في PostgreSQL

تطوير موضوع القطعة الموسيقية على تنفيذ صف مستوى Secutity في كيو و للإجابة مفصلة لل تعليق.



تتضمن إستراتيجية الاستخدام استخدام مفهوم "منطق الأعمال في قاعدة البيانات" ، والذي تم وصفه بالتفصيل هنا - دراسة حول تنفيذ منطق الأعمال المخزن في وظائف PostgreSQL ،



الجزء النظري الموصوف تمامًا في وثائق خطوط Postgres Pro - Protection Policies . فيما يلي نأخذ في الاعتبار التنفيذ العملي لمهمة عمل محددة - نموذج الوصول إلى البيانات المستند إلى الدور.





المقال ليس بجديد ، فلا معنى خفي ومعرفة سرية. مجرد رسم تخطيطي حول التنفيذ العملي لفكرة نظرية. إذا كان أي شخص مهتم ، واصل القراءة. من غير مهتم - لا تضيعوا وقتكم.


صياغة المشكلة



من الضروري تحديد الوصول لعرض / إدراج / تعديل / حذف مستند وفقًا لدور مستخدم التطبيق. الدور يعني إدخالاً في جدول الأدوار مرتبط بعلاقة أطراف بأطراف مع جدول المستخدمين . تم حذف تفاصيل تنفيذ الجداول بسبب التفاهة. أيضًا ، تم حذف تفاصيل التنفيذ المحددة المتعلقة بالمجال.



التنفيذ



نقوم بإنشاء الأدوار والمخططات والجدول



إنشاء كائنات قاعدة البيانات
CREATE ROLE store;
CREATE SCHEMA store AUTHORIZATION store;
CREATE TABLE store.docs
(
  id integer ,         --id 
  man_id integer , --id  
  stat_id integer ,  --id  
  ...
  is_del BOOLEAN DEFAULT FALSE 
);
ALTER TABLE store.docs ADD CONSTRAINT doc_pk PRIMARY KEY (id);
ALTER TABLE store.docs OWNER TO store ;


إنشاء وظائف لتنفيذ RLS



التحقق مما إذا كان الصف هو SELECT



check_select
CREATE OR REPLACE FUNCTION store.check_select ( current_id store.docs.id%TYPE ) RETURNS boolean AS $$
DECLARE
  result boolean ;
  curr_pid integer ;
  curr_stat_id integer ;
  doc_man_id integer ;
BEGIN 
  -- DBA     
  IF SESSION_USER = 'curr_dba'
  THEN
    RETURN TRUE ;
  END IF ;
  --------------------------------

  --    '' -    
  SELECT
    is_del
  INTO
    result
  FROM
    store.docs
  WHERE
    id = current_id ;
 IF result = TRUE
 THEN
   RETURN FALSE ;
 END IF ;
 --------------------------------

 -- id  
 SELECT
   service_function.get_curr_pid ()
 INTO
   curr_pid ;
 --------------------------------

 -- id  
 SELECT
   man_id
 INTO
   doc_man_id
 FROM
   store.docs
 WHERE
   id = current_id ;
 --------------------------------

 --         
 --   
 IF doc_man_id != curr_pid OR doc_man_id IS NULL
 THEN
   RETURN TRUE  ;
 ELSE
   --   
   SELECT
     stat_id                                         
   INTO
     curr_statid
   FROM
     store.docs
   WHERE
     id = current_id ;
    
   --     -                         
   IF curr_statid = 4 OR curr_statid = 9
   THEN
     RETURN TRUE ;
   ELSE
   -- -    
     RETURN FALSE ;
    END IF ;
  END IF ;
  --------------------------------

 RETURN FALSE ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;
ALTER FUNCTION store.check_select( store.docs.id%TYPE  ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.check_select( store.docs.id%TYPE  ) FROM public; 
GRANT EXECUTE ON FUNCTION store.check_select( store.docs.id%TYPE  ) TO service_functions; 


التحقق من وجود صفوف INSERT



check_insert
CREATE OR REPLACE FUNCTION store.check_insert ( current_id store.docs.id%TYPE ) RETURNS boolean AS $$
DECLARE
  curr_role_id integer ;
BEGIN
  --DBA      
  IF SESSION_USER = 'curr_dba'
  THEN
    RETURN TRUE ;
  END IF ;
  --------------------------------

 -- id    
 SELECT
   service_functions.current_rid()
  INTO
    curr_role_id ;
 --------------------------------

--      
--
IF curr_role_id = 3 OR curr_role_id = 5     
THEN
  RETURN TRUE ;
END IF ;
--------------------------------
RETURN FALSE  ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;
ALTER FUNCTION store.check_insert( store.docs.id%TYPE  ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.check_insert( store.docs.id%TYPE  ) FROM public;
GRANT EXECUTE ON FUNCTION store.check_insert( store.docs.id%TYPE  ) TO service_functions; 


التحقق من حذف الصفوف



check_delete
CREATE OR REPLACE FUNCTION store.check_delete ( current_id store.docs.id%TYPE )
RETURNS boolean AS $$
BEGIN  
  -- DBA    
  IF SESSION_USER = 'curr_dba'
  THEN
    RETURN TRUE ;
  END IF ;
  --------------------------------

  RETURN FALSE ;
END
$$ LANGUAGE plpgsql
SECURITY DEFINER;
ALTER FUNCTION store.check_delete( store.docs.id%TYPE  ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.check_delete( store.docs.id%TYPE  ) FROM public;


التحقق مما إذا كان الصف يمكن أن يكون UPDATE.



باستخدام التحديث
CREATE OR REPLACE FUNCTION store.update_using ( current_id store.docs.id%TYPE , is_del boolean  )
RETURNS boolean AS $$
BEGIN  
   --   '' -  
   IF is_del 
   THEN
     RETURN FALSE ;
 ELSE
    RETURN TRUE ;
  END IF ;

END
$$ LANGUAGE plpgsql SECURITY DEFINER;
ALTER FUNCTION store.update_using(  store.docs.id%TYPE ,  boolean  ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.update_using(  store.docs.id%TYPE ,  boolean  ) FROM public;
GRANT EXECUTE ON FUNCTION store.update_using( store.docs.id%TYPE  ) TO service_functions;


update_check
CREATE OR REPLACE FUNCTION store.update_with_check ( current_id store.docs.id%TYPE , is_del boolean )
RETURNS boolean AS $$
DECLARE
  current_rid integer ;
  current_statid integer ;
BEGIN                

  --DBA    
  IF SESSION_USER = 'curr_dba'
  THEN
    RETURN TRUE ;
  END IF ;
  --------------------------------

 -- id    
 SELECT
   service_functions.current_rid()
  INTO
    curr_role_id ;
 --------------------------------                            

 --  -   
 IF is_deleted
 THEN
   --   ***
   IF current_role_id = 3        
   THEN
      SELECT
        stat_id                                          
      INTO
        curr_statid
      FROM
        store.docs
      WHERE
        id = current_id ;

      --   ***   
      IF current_status_id = 11
      THEN
         RETURN FALSE ;
      ELSE
      --     
        RETURN TRUE ;
      END IF ;

    -- ,    ***
    ELSIF current_role_id = 5            
    THEN
      --   
      RETURN TRUE ;
    ELSE
      --     
      RETURN FALSE ;
    END IF ;
 ELSE      
   --  
    RETURN TRUE ;
END IF ;

RETURN FALSE ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;
ALTER FUNCTION store.update_with_check( storg.docs.id%TYPE ,  boolean   ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.update_with_check( storg.docs.id%TYPE ,  boolean   )  FROM public;
GRANT EXECUTE ON FUNCTION store.update_with_check( store.docs.id%TYPE  ) TO service_functions;


تمكين سياسة أمان مستوى الصف للجدول.



تمكين أمان مستوى الصف
ALTER TABLE store.docs ENABLE ROW LEVEL SECURITY ;

CREATE POLICY doc_select ON store.docs FOR SELECT TO service_functions USING ( (SELECT store.check_select(id)) );
CREATE POLICY doc_insert ON store.docs FOR INSERT TO service_functions WITH CHECK ( (SELECT store.check_insert(id)) );
CREATE POLICY docs_delete ON store.docs FOR DELETE TO service_functions USING ( (SELECT store.check_delete(id)) );

CREATE POLICY doc_update_using ON store.docs FOR UPDATE TO service_functions USING ( (SELECT store.update_using(id , is_del )) );
CREATE POLICY doc_update_check ON store.docs FOR UPDATE TO service_functions  WITH CHECK ( (SELECT store.update_with_check(id , is_del )) );


النتيجة



إنها تعمل.



جعلت الاستراتيجية المقترحة من الممكن نقل تنفيذ نموذج الدور من مستوى وظائف العمل إلى مستوى تخزين البيانات.



يمكن استخدام الوظائف كقالب لتنفيذ نماذج أكثر تعقيدًا لإخفاء البيانات إذا كانت متطلبات العمل تتطلب ذلك.



All Articles