পোস্টগ্র্রেএসকিউএল এ দেখা এবং ট্রিগারগুলির মাধ্যমে বর্তমান ব্যবহারকারীর সন্ধান করা


11

আমার কাছে একটি পোস্টগ্রিজ এসকিউএল (9.4) ডাটাবেস রয়েছে যা বর্তমান ব্যবহারকারীর উপর নির্ভর করে রেকর্ডে অ্যাক্সেসকে সীমাবদ্ধ করে এবং ব্যবহারকারীর দ্বারা পরিবর্তনগুলি ট্র্যাক করে। এটি ভিউ এবং ট্রিগারগুলির মাধ্যমে অর্জন করা হয়েছে এবং বেশিরভাগ অংশে এটি ভালভাবে কাজ করে তবে আমার এমন INSTEAD OFভিউগুলিতে সমস্যা হচ্ছে যার জন্য ট্রিগারগুলি দরকার। আমি সমস্যাটি হ্রাস করার চেষ্টা করেছি, তবে আমি আগে থেকে ক্ষমা চেয়েছি যে এটি এখনও বেশ দীর্ঘ।

পরিস্থিতি

ডাটাবেসের সমস্ত সংযোগগুলি একটি একাউন্টের মাধ্যমে ওয়েব ফ্রন্ট-এন্ড থেকে তৈরি করা হয় dbweb। একবার সংযুক্ত হয়ে গেলে, SET ROLEওয়েব ইন্টারফেস ব্যবহার করা ব্যক্তির সাথে সম্পর্কিত হয়ে ভূমিকাটি পরিবর্তিত হয়, এবং এই জাতীয় সমস্ত ভূমিকা গোষ্ঠীর ভূমিকা সম্পর্কিত dbuser। ( বিস্তারিত জানার জন্য এই উত্তরটি দেখুন )। ধরে নেওয়া যাক ব্যবহারকারীর হয় alice

আমার বেশিরভাগ টেবিলগুলি এমন একটি স্কিমাতে স্থাপন করা হয়েছে যা এখানে আমি কল করব privateএবং এর সাথে সম্পর্কিত dbowner। এই টেবিলগুলি সরাসরি অ্যাক্সেসযোগ্য নয় dbuser, তবে অন্য ভূমিকার জন্য dbview। উদাহরণ:

SET SESSION AUTHORIZATION dbowner;
CREATE TABLE private.incident
(
  incident_id serial PRIMARY KEY,
  incident_name character varying NOT NULL,
  incident_owner character varying NOT NULL
);
GRANT ALL ON TABLE private.incident TO dbview;

বর্তমান ব্যবহারকারীর জন্য নির্দিষ্ট সারিগুলির উপলব্ধতা aliceঅন্যান্য দর্শন দ্বারা নির্ধারিত হয়। সরলীকৃত উদাহরণ (যা হ্রাস করা যেতে পারে তবে আরও সাধারণ ক্ষেত্রে সমর্থন করার জন্য এই পদ্ধতিটি করা দরকার) হ'ল:

-- Simplified case, but in principle could join multiple tables to determine allowed ids
CREATE OR REPLACE VIEW usr_incident AS 
 SELECT incident_id
   FROM private.incident
  WHERE incident_owner  = current_user;
ALTER TABLE usr_incident
  OWNER TO dbview;

এরপরে সারিগুলিতে অ্যাক্সেস প্রদান করা হয় যা dbuserভূমিকায় যেমন অ্যাক্সেসযোগ্য যেমন alice:

CREATE OR REPLACE VIEW public.incident AS 
 SELECT incident.*
   FROM private.incident
  WHERE (incident_id IN ( SELECT incident_id
           FROM usr_incident));
ALTER TABLE public.incident
  OWNER TO dbview;
GRANT ALL ON TABLE public.incident TO dbuser;

নোট করুন যেহেতু কেবলমাত্র একটি সম্পর্কের FROMধারাটিতে উপস্থিত হয়েছে , এই ধরণের দৃশ্যটি কোনও অতিরিক্ত ট্রিগার ছাড়াই আপডেটযোগ্য।

লগিংয়ের জন্য, কোন টেবিলটি পরিবর্তন হয়েছে এবং কে এটি পরিবর্তন করেছে তা রেকর্ড করতে অন্য সারণী উপস্থিত রয়েছে। একটি হ্রাস সংস্করণ হ'ল:

CREATE TABLE private.audit
(
  audit_id serial PRIMATE KEY,
  table_name text NOT NULL,
  user_name text NOT NULL
);
GRANT INSERT ON TABLE private.audit TO dbuser;

আমি ট্র্যাক করতে ইচ্ছুক প্রতিটি সম্পর্কের উপর ট্রিগারগুলির মাধ্যমে এটি পপুলস। উদাহরণস্বরূপ, private.incidentকেবল সন্নিবেশগুলিতে সীমাবদ্ধ থাকার জন্য একটি উদাহরণ :

CREATE OR REPLACE FUNCTION private.if_modified_func()
  RETURNS trigger AS
$BODY$
BEGIN
    IF TG_OP = 'INSERT' THEN
        INSERT INTO private.audit (table_name, user_name)
        VALUES (tg_table_name::text, current_user::text);
        RETURN NEW;
    END IF;
END;
$BODY$
  LANGUAGE plpgsql;
GRANT EXECUTE ON FUNCTION private.if_modified_func() TO dbuser;

CREATE TRIGGER log_incident
AFTER INSERT ON private.incident
FOR EACH ROW
EXECUTE PROCEDURE private.if_modified_func();

সুতরাং এখন যদি aliceserোকানো হয় public.incident, একটি রেকর্ড ('incident','alice')অডিট প্রদর্শিত হবে।

সমস্যাটি

যখন ভিউগুলি আরও জটিল হয়ে যায় এবং INSTEAD OFসন্নিবেশগুলিকে সমর্থন করার জন্য ট্রিগারগুলির প্রয়োজন হয় তখন এই পদ্ধতির সমস্যার সৃষ্টি হয় ।

ধরা যাক আমার দু'টি সম্পর্ক রয়েছে, উদাহরণস্বরূপ কিছু কিছু-টু-ওয়ান সম্পর্কের সাথে জড়িত সত্তাদের প্রতিনিধিত্ব করুন:

CREATE TABLE private.driver
(
  driver_id serial PRIMARY KEY,
  driver_name text NOT NULL
);
GRANT ALL ON TABLE private.driver TO dbview;

CREATE TABLE private.vehicle
(
  vehicle_id serial PRIMARY KEY,
  incident_id integer REFERENCES private.incident,
  make text NOT NULL,
  model text NOT NULL,
  driver_id integer NOT NULL REFERENCES private.driver
);
GRANT ALL ON TABLE private.vehicle TO dbview;

ধরে নিন যে আমি নাম বাদে অন্য বিবরণগুলি প্রকাশ করতে চাই না private.driver, এবং তাই এমন একটি দৃষ্টিভঙ্গি রয়েছে যা টেবিলগুলিতে যোগ হয় এবং আমি যে বিটগুলি প্রকাশ করতে চাই তা প্রজেক্টগুলিতে যোগ দেয়:

CREATE OR REPLACE VIEW public.vehicle AS 
 SELECT vehicle_id, make, model, driver_name
   FROM private.driver
   JOIN private.vehicle USING (driver_id)
  WHERE (incident_id IN ( SELECT incident_id
               FROM usr_incident));
ALTER TABLE public.vehicle OWNER TO dbview;
GRANT ALL ON TABLE public.vehicle TO dbuser;

জন্য জন্য aliceএই দৃশ্য একটি ট্রিগার প্রদান করা হয়েছে, যেমন মধ্যে সন্নিবেশ পাবে:

CREATE OR REPLACE FUNCTION vehicle_vw_insert()
  RETURNS trigger AS
$BODY$
DECLARE did INTEGER;
   BEGIN
     INSERT INTO private.driver(driver_name) VALUES(NEW.driver_name) RETURNING driver_id INTO did;
     INSERT INTO private.vehicle(make, model, driver_id) VALUES(NEW.make_id,NEW.model, did) RETURNING vehicle_id INTO NEW.vehicle_id;
     RETURN NEW;
    END;
$BODY$
  LANGUAGE plpgsql SECURITY DEFINER;
ALTER FUNCTION vehicle_vw_insert()
  OWNER TO dbowner;
GRANT EXECUTE ON FUNCTION vehicle_vw_insert() TO dbuser;

CREATE TRIGGER vehicle_vw_insert_trig
INSTEAD OF INSERT ON public.vehicle
FOR EACH ROW
EXECUTE PROCEDURE vehicle_vw_insert();

SECURITY DEFINERএটির সাথে সমস্যাটি হ'ল ট্রিগার ফাংশনে বিকল্পটি এটি current_userসেট দিয়ে চালিত হওয়ার কারণ হয়ে dbownerথাকে, সুতরাং লেখককে রেকর্ডের ক্ষেত্রে aliceসংশ্লিষ্ট এন্ট্রি ভিউতে একটি নতুন রেকর্ড সন্নিবেশ করানো হয় ।private.auditdbowner

সুতরাং, সেখানে সংরক্ষণ করা একটি উপায় current_userপ্রদান ছাড়া, dbuserস্কিমা মধ্যে সম্পর্ক গোষ্ঠী ভূমিকা সরাসরি প্রবেশাধিকার private?

আংশিক সমাধান

ক্রেগের পরামর্শ অনুসারে, ট্রিগারগুলির চেয়ে নিয়ম ব্যবহার করা পরিবর্তনগুলি এড়ানো যায় current_user। উপরের উদাহরণটি ব্যবহার করে নিম্নলিখিতটি ট্রিগারের পরিবর্তে ব্যবহার করা যেতে পারে:

CREATE OR REPLACE RULE update_vehicle_view AS
  ON UPDATE TO vehicle
  DO INSTEAD
     ( 
      UPDATE private.vehicle
        SET make = NEW.make,
            model = NEW.model
      WHERE vehicle_id = OLD.vehicle_id
       AND (NEW.incident_id IN ( SELECT incident_id
                   FROM usr_incident));
     UPDATE private.driver
        SET driver_name = NEW.driver_name
       FROM private.vehicle v
      WHERE driver_id = v.driver_id
      AND vehicle_id = OLD.vehicle_id
      AND (NEW.incident_id IN ( SELECT incident_id
                   FROM usr_incident));               
   )

এই সংরক্ষণ current_user। সমর্থনমূলক RETURNINGধারাগুলি কিছুটা লোমশ হতে পারে, যদিও। তদ্ব্যতীত, সিক্যুয়েন্সের ব্যবহারটি পরিচালনা করার জন্য আমি একই সাথে উভয় টেবিলের মধ্যে সন্নিবেশ করানোর নিয়ম ব্যবহার করার কোনও নিরাপদ উপায় খুঁজে পাই না driver_id। সবচেয়ে সহজ উপায় ব্যবহার করতে হতো WITHএকটি ইন দফা INSERT(কোটে), কিন্তু এই সাথে অনুমতি দেওয়া হয় না NEW(ত্রুটি: rules cannot refer to NEW within WITH query), রিসোর্ট এক ছাড়ার lastval()যা হয় জোরালোভাবে নিরুৎসাহিত

উত্তর:


4

সুতরাং, current_userডুবার গ্রুপের ভূমিকাটি স্কিমায় ব্যক্তিগত ক্ষেত্রে সম্পর্কের সরাসরি অ্যাক্সেস না দিয়ে কি সংরক্ষণের কোনও উপায় আছে ?

আপনি INSTEAD OFভিউর মাধ্যমে লেখার অ্যাক্সেস সরবরাহ করতে একটি ট্রিগার না দিয়ে একটি বিধি ব্যবহার করতে সক্ষম হতে পারেন । ভিউগুলি সর্বদা অনুসন্ধানকারী ব্যবহারকারীর চেয়ে ভিউ ক্রিয়েটারের সুরক্ষা অধিকারের সাথে কাজ করে তবে আমি মনে করি এটি current_user পরিবর্তন হয় না ।

আপনার অ্যাপ্লিকেশনটি যদি সরাসরি ব্যবহারকারী হিসাবে সংযোগ করে তবে আপনি session_userতার পরিবর্তে চেক করতে পারেন current_user। আপনি যদি কোনও জেনেরিক ব্যবহারকারীর সাথে সংযোগ স্থাপন করেন তবে এটিও কাজ করে SET SESSION AUTHORIZATION। যদিও আপনি সাধারণ ব্যবহারকারী হিসাবে SET ROLEকাঙ্ক্ষিত ব্যবহারকারীর সাথে সংযোগ স্থাপন করেন তবে এটি কাজ করবে না ।

কোনও SECURITY DEFINERফাংশন থেকে তত্ক্ষণাত্ পূর্ববর্তী ব্যবহারকারীকে পাওয়ার কোনও উপায় নেই । আপনি কেবল current_userএবং পেতে পারেন session_userlast_userব্যবহারকারীর পরিচয়গুলির একটি স্ট্যাক বা স্ট্যাক পাওয়ার একটি উপায় দুর্দান্ত হবে তবে বর্তমানে এটি সমর্থিত নয়।


আহা, এর আগে নিয়ম নিয়ে কাজ করিনি, ধন্যবাদ। SET SESSIONআরও ভাল হতে পারে তবে আমি মনে করি প্রাথমিক লগইন ব্যবহারকারীর জন্য অতিরিক্ত ব্যবহারকারীর সুবিধাগুলি থাকা দরকার, যা গন্ধযুক্ত গন্ধযুক্ত।
বেলডাজ 12'15

@ বেলডাজ হ্যাঁ এটা বড় সমস্যা SET SESSION AUTHORIZATION। আমি এর মধ্যে সত্যই কিছু চাই এবং SET ROLEতবে এই মুহুর্তে এ জাতীয় কোনও জিনিস নেই।
ক্রেগ রিঞ্জার

1

সম্পূর্ণ উত্তর নয়, তবে এটি কোনও মন্তব্যে মাপসই হবে না।

lastval() & currval()

আপনি কি lastval()নিরুৎসাহিত মনে করেন ? ভুল বোঝাবুঝির মতো মনে হচ্ছে।

ইন রেফারেন্সড উত্তর ক্রেইগ জোরালোভাবে একটি শাসনের পরিবর্তে একটি ট্রিগার ব্যবহার করতে পরামর্শ দেওয়া হচ্ছে মন্তব্য । এবং আমি সম্মত - আপনার বিশেষ কেস বাদে স্পষ্টতই।

উত্তর জোরালোভাবে ব্যবহার discourages currval()- কিন্তু যে একটি misundertstanding মনে করা হয়। এর সাথে lastval()বা বরং কিছু ভুল নেই currval()। আমি রেফারেন্স করা উত্তর সহ একটি মন্তব্য রেখেছি।

ম্যানুয়ালটি উদ্ধৃত করা হচ্ছে:

currval

nextvalবর্তমান সেশনে এই সিকোয়েন্সটির জন্য সর্বাধিক প্রাপ্ত মানটি ফিরিয়ে দিন। ( nextvalএই অধিবেশনে কখনও এই ক্রমের জন্য ডাকা না হলে একটি ত্রুটি প্রতিবেদন করা হয়)) কারণ এটি একটি সেশন-স্থানীয় মানটি ফিরিয়ে nextvalদিচ্ছে, এটি বর্তমান অধিবেশনটি করার পর থেকে অন্যান্য সেশনগুলি কার্যকর হয়েছে কি না তা অনুমানযোগ্য উত্তর দেয় ।

সুতরাং এটি সমবর্তী লেনদেনের সাথে নিরাপদ। অবিচ্ছিন্নভাবে একই ট্রিগারটিকে কল করতে পারে এমন অন্যান্য ট্রিগার বা নিয়ম থেকে একমাত্র সম্ভাব্য জটিলতা দেখা দিতে পারে - এটি একটি অত্যন্ত সম্ভাবনাময় দৃশ্য এবং আপনি কোন ট্রিগার / বিধিগুলি ইনস্টল করেন তার উপর আপনার সম্পূর্ণ নিয়ন্ত্রণ রয়েছে।

তবে , আমি নিশ্চিত না যে কমান্ডের অনুক্রম নিয়মের মধ্যে সংরক্ষিত রয়েছে (যদিও currval()এটি উদ্বায়ী ফাংশন হিসাবে )। এছাড়াও, একটি বহু-সারি INSERTআপনাকে সিঙ্ক থেকে বেরিয়ে আসতে পারে। আপনি আপনার নিয়মকে দুটি নিয়মে বিভক্ত করতে পারেন, কেবল দ্বিতীয়টি INSTEAD। মনে রাখবেন, প্রতি ডকুমেন্টেশন:

একই টেবিলে একাধিক নিয়ম এবং একই ইভেন্টের ধরণের বর্ণমালার নাম ক্রমে প্রয়োগ করা হয়।

সময়ের বাইরে আমি আর তদন্ত করিনি।

DEFAULT PRIVILEGES

জন্য:

SET SESSION AUTHORIZATION dbowner;
...
GRANT ALL ON TABLE private.incident TO dbview;

পরিবর্তে আপনার আগ্রহী হতে পারে:

ALTER DEFAULT PRIVILEGES FOR ROLE dbowner IN SCHEMA private
   GRANT ALL ON TABLES TO dbview;

সম্পর্কিত:


ধন্যবাদ, আমি বুঝতে আমার বুঝতে ভুল ছিলাম lastvalএবং currvalযেহেতু আমি বুঝতে পারি নি যে তারা একটি সেশনে স্থানীয় ছিল। আমি আসলে আমার আসল স্কিমাতে ডিফল্ট সুবিধাগুলি ব্যবহার করি তবে প্রতি টেবিলগুলি ডাম্পড ডিবি থেকে অনুলিপি এবং আটকানো থেকে আসে। আমি এই সিদ্ধান্তে পৌঁছেছি যে সম্পর্কের পুনর্গঠন নিয়মগুলি নিয়ে ঝগড়া করার চেয়ে সহজ, যদিও সেগুলি সুস্পষ্ট, কারণ আমি দেখতে পাচ্ছি যে পরে তাদের মাথা ব্যথা হতে পারে।
বেলডাজ

@ বেলডাজ: আমি মনে করি এটি একটি ভাল সিদ্ধান্ত। আপনার নকশাটি খুব জটিল হয়ে উঠছিল।
এরউইন ব্র্যান্ডসটেটার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.