Ik heb het in PHP al voor elkaar, alleen soms zal er waarschijnlijk de behoefte ontstaan om alleen die resources op te halen uit mijn database waarvoor de gebruiker die ze opvraagt voldoende rechten heeft. Ik wil dus de selectie al in mijn database maken en niet achteraf filteren.
Hiertoe heb ik de volgende MySQL functie gemaakt (zowel deze code als de PHP variant volgt in grote lijnen de abstracte implementatie op de WIKI-pagina):
DELIMITER ;;
DROP FUNCTION IF EXISTS has_rights;;
CREATE FUNCTION has_rights(expression VARCHAR(255), rights VARCHAR(255)) RETURNS BOOL DETERMINISTIC
BEGIN
DECLARE stack VARCHAR(255);
DECLARE token VARCHAR(5);
DECLARE val1 VARCHAR(5);
DECLARE val2 VARCHAR(5);
IF (expression = '') THEN
RETURN TRUE;
END IF;
SET stack = '1,';
SET expression = REVERSE(CONCAT(',&,', expression));
WHILE (LOCATE(',', expression) > 0) DO
SET token = SUBSTRING_INDEX(expression, ',', 1);
SET expression = SUBSTRING(expression, LENGTH(token) + 2);
IF (LENGTH(token) > 0) THEN
IF (token = '|' OR token = '&') THEN
SET val1 = SUBSTRING_INDEX(stack, ',', 1);
SET stack = SUBSTRING(stack, LENGTH(val1) + 2);
SET val2 = SUBSTRING_INDEX(stack, ',', 1);
SET stack = SUBSTRING(stack, LENGTH(val2) + 2);
IF (token = '|') THEN
SET stack = CONCAT((val1 OR val2), ',', stack);
ELSE
SET stack = CONCAT((val1 AND val2), ',', stack);
END IF;
ELSEIF (token = '!') THEN
SET val1 = SUBSTRING_INDEX(stack, ',', 1);
SET stack = CONCAT(NOT(val1), ',', SUBSTRING(stack, LENGTH(val1) + 2));
ELSE
SET val1 = LOCATE(CONCAT(',', token, ','), CONCAT(',', rights, ',')) > 0;
SET stack = CONCAT(val1, ',', stack);
END IF;
END IF;
END WHILE;
SET val1 = SUBSTRING_INDEX(stack, ',', 1);
RETURN (val1 = 1);
END;;
DELIMITER ;Hierbij heb ik wel wat truuks uit moeten halen en moet ik bepaalde randgevallen correct afhandelen. Bovenstaande code kan desgewenst worden toegelicht.
Omdat dit wellicht nogal abstract is een voorbeeld.
Stel ik heb een resource die als volgt is ingesteld: de resource is toegankelijk als iemand recht 1 heeft, OF recht 2, maar dan mag die persoon het recht 3 niet hebben. De "rechtenboom" ziet er dan als volgt uit:
OR--+--1
|
+--AND--2
|
+--NOT--3Deze kun je ook op de bovenstaande pagina nabouwen (give it a try).
De geserialiseerde expressie (in prefix notatie) wordt dan (| is de logische OR, & de logische AND, ! de logische NOT):
|,1,&,2,!,3Als we vervolgens wat SELECTs doen:
mysql> SELECT has_rights('|,1,&,2,!,3', '1');
+--------------------------------+
| has_rights('|,1,&,2,!,3', '1') |
+--------------------------------+
| 1 |
+--------------------------------+
1 row in set (0.00 sec)(gebruiker heeft recht 1 - toegang verleend)
mysql> SELECT has_rights('|,1,&,2,!,3', '2');
+--------------------------------+
| has_rights('|,1,&,2,!,3', '2') |
+--------------------------------+
| 1 |
+--------------------------------+
1 row in set (0.00 sec)(gebruiker heeft recht 2 - toegang verleend)
mysql> SELECT has_rights('|,1,&,2,!,3', '2,3');
+----------------------------------+
| has_rights('|,1,&,2,!,3', '2,3') |
+----------------------------------+
| 0 |
+----------------------------------+
1 row in set (0.00 sec)(gebruiker heeft recht 2 en 3 - toegang geweigerd)
Nu heb ik wat vragen:
- wat vind je van dit idee (een geserialiseerd predikaat opslaan bij een resource die aangeeft wat (moet blijken uit de kolom waar je dit in opslaat) en onder welke condities (moet blijken uit de validatie middels has_rights()) hier iets mee gedaan mag worden)
- MySQL kent geen arrays, een alternatief hiervoor zijn tijdelijke tabellen ofzo (dan moet ik er waarschijnlijk een PROCEDURE van maken); bovenstaande code is een beetje wollig, maar lijkt te werken; zijn er ergens nog dingen aan te verbeteren / te veranderen?