Ik ben bezig aan een CMS waarin meerdere websites op dezelfde database en dezelfde serie bestanden draaien. De teksten, gebruikers e.d. van verschillende websites komen dus allemaal in dezelfde tabellen te staan op de server.

Nu loop ik tegen het probleem aan dat de websitebeheerders, als zij inloggen op de database, alle records van die tabellen kunnen lezen. Ze kunnen dus ook de teksten op andere websites zien (ook als die op de website zelf achter een inlogsysteem zitten). Dat is uiteraard niet de bedoeling.

Om dat te voorkomen vatte ik het wilde plan op om records aan databasegebruikers te koppelen, dus dat je een bepaalde gebruiker de eigendomsrechten van een bepaalde rij uit een tabel geeft. Elke websitebeheerder heeft FTP-toegang tot een index.php op zijn website, die eigenlijk niks meer doet dan het CMS includen. In dat bestandje kan ik ook databasegegevens opslaan, zodat elke website een eigen account heeft op de databaseserver. Maar de beheerders kunnen dus ook phpMyAdmin uploaden naar hun FTP-server en vervolgens met de gebruikersgegevens direct inloggen op de database om alle records te lezen én te wijzigen.

Alleen is er volgens mij geen doorsnee database engine die gebruikersrechten per record ondersteunt. Ik heb even gekeken naar pgSQL (die voor zover ik weet qua rechten etc. de meeste mogelijkheden biedt), maar die kan het niet.

Weet iemand toevallig hoe ik zoiets zou kunnen realiseren? Ik zou natuurlijk per website een database aan kunnen maken, maar omdat het systeem steeds geüpdate wordt als het al in gebruik is, betekent dat dat ik bij elke update alle databases langs moet lopen om wijzigingen in de structuur aan te brengen. Vandaar dat ik die optie voor het laatst bewaar.

[edit]Even iets duidelijker geprobeerd te omschrijven.[/edit]
Je bent dan verplicht om stored procedures te gebruiken en per gebruiker een eigen account te maken. In pgSQL werkt dat perfect, is volledig waterdicht.

Met views en rules kan het ook, maar rules vind ik nogal ***beep*** werken, vandaar dat mijn voorkeur uitgaat naar het gebruik van sp's.

Ik ken trouwens geen enkele database waarbij je op recordniveau rechten kunt uitdelen.
pgFrank schreef op 15.01.2009 15:58
Je bent dan verplicht om stored procedures te gebruiken en per gebruiker een eigen account te maken.

Kun je iets meer uitleggen hoe ik dit kan gebruiken?
Ik zal eens een voorbeeldje inelkaar draaien.

Edit:

Aanmaken tabelletje waar de data in komt:

CREATE TABLE berichten
(
  id serial NOT NULL,
  eigenaar text DEFAULT session_user,
  bericht text NOT NULL,
  CONSTRAINT berichten_pkey PRIMARY KEY (id)
)
WITH (OIDS=FALSE);
ALTER TABLE berichten OWNER TO postgres;
GRANT ALL ON TABLE berichten TO postgres;

De user "postgres" is superuser en mag er als enige bijkomen.

Dan de functie om data in deze tabel weg te schrijven:

CREATE OR REPLACE FUNCTION add_bericht("content" text)
  RETURNS boolean AS
$BODY$
BEGIN
	INSERT INTO berichten (eigenaar, bericht) VALUES(session_user, content);

	RETURN true;
END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE SECURITY DEFINER
  COST 100;
ALTER FUNCTION add_bericht(text) OWNER TO postgres;

En het instellen van de rechten voor zowel public (iedereen) als postgres:

GRANT EXECUTE ON FUNCTION add_bericht(text) TO public;
GRANT EXECUTE ON FUNCTION add_bericht(text) TO postgres;


En de functie om de data op te halen:

CREATE OR REPLACE FUNCTION get_berichten(OUT "content" text)
  RETURNS SETOF text AS
$BODY$
DECLARE
	query	text;
	row		record;
BEGIN
	query := 'SELECT bericht FROM berichten WHERE eigenaar = ''' || session_user || '''';

	FOR row IN EXECUTE query LOOP
		content := row.bericht;
		RETURN NEXT;
	END LOOP;
	
	RETURN;
END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE SECURITY DEFINER
  COST 100
  ROWS 1000;
ALTER FUNCTION get_berichten() OWNER TO postgres;

En de rechten:

GRANT EXECUTE ON FUNCTION get_berichten() TO public;
GRANT EXECUTE ON FUNCTION get_berichten() TO postgres;

Vervolgens maak je nog even een paar extra rollen aan:

CREATE ROLE frank LOGIN
  PASSWORD 'frank'
  NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE;
CREATE ROLE erik LOGIN
  PASSWORD 'erik'
  NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE;
CREATE ROLE hans LOGIN
  PASSWORD 'hans'
  NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE;

Zorg er voor dat IEDERE user een eigen account krijgt, dat is onmisbaar bij het herkennen van de user. Wanneer je niet weet wie er is ingelogd, weet je ook niet of je de juiste data te pakken hebt. Dat lijkt me logisch.

Dankzij de constante "session_user" wordt er bij ieder record de eigenaar opgeslagen en kan je later dus weer per eigenaar de gewenste data ophalen. Ga er eens mee stoeien, werkt prima.

Zorg er wel voor dat er niemand in de tabellen kan komen, controleer dus nog even of er geen public rechten zijn op de tabellen. Daarmee heb je ook direct het minpunt van pgSQL te pakken, er worden teveel public rechten uitgedeeld bij het aanmaken van objecten. Hopelijk gooien ze die onzin er nog eens uit.
Klaar!
Ok, klinkt veelbelovend! De boel wordt er zeker wel wat langzamer van? Nja, dat moet ik dan maar op de koop toe nemen ;-)

Bedankt in ieder geval, dit ga ik eens even goed bekijken!
Waarom zou het langzamer worden? Tuurlijk, je hebt een query met een where, maar dat is dan ook alles.

Mocht de query wat langzaam worden, ga dan met EXPLAIN eens kijken waarom dat zo is. Waarschijnlijk moet je een index zetten op de kolom "eigenaar", dat maakt zoeken op eigenaar een stuk sneller.

Succes!

Reageren