ACL szűrt listázás MySql-ből

Van arra értelmes megoldás, hogy hogy lehet megkapni db-ből azt az n darab tartalmat, ami hozzáférhető egy adott user számára - megfejelve ezt a user számára hozzáférhető tartalmak számával (SQL_CALC_FOUND_ROWS)? Az ACL szabályok túl komplikáltak ahhoz, hogy injektálhatóak legyenek a lekérdezésbe, csak az alkalmazás szintjén értelmezhetőek. Periódikasan futassam a query-t amíg nem kapok n darab olyan tartalmat amit átenged az ACL? És hogy állapítom meg az összes hozzáférhető tartalom számát (pl. a lapozáshoz)? Vagy az ACL- t implementáljam az adatbázisba és hivogassak tárolt eljárásokat? Egyáltalán ez a probléma megoldható vagy csak megkerülhető?

Hozzászólások

öh... mi a kérdés? milyen ACL? hogy van felépítve?
tarolt eljárás jó ötlet.

Még sehogy. De komplikáltak lesznek, az latszik. Bocs ha túl általános a kérdés, de konkrétabban nem tudom megfogalmazni. pl ilyen szabályok halamaza a vonakozhat egy usere:
Ha user szuloje beletartozik x csoportba akkor ne tudja szerekeszteni a szulo tartalmait. Vagy x user minden szabalyon felul tudjon szerkeszteni tartalmat ami két dátum között keletkezett. Ezek csak otletek, de kb ilyen szintu dologra kell szamitani.
Gondolom ez nem egyedi problema, nyilvan mindenhol belefutnak ahol bármilyen ACL-t használnak.

Jelen pillantaban annyit kell az ACL -ről tudni, hogy atadok neki egy userId-t, egy contentId-t [es esteleg egy művelet azonosítót] és visszakapok true v. false -t. Ki akarok listazni 10 tartalom címet amit a user szerkeszthet. Ha lekerek a db-bol 10 tartalmat és vegigzavarom az ACL -n lehet mind a 10-t elutasítja. Akkor johet a kovetkezo adag egeszen addig amíg ossze nem gyulik 10 amit atengedett. Ez meg ok lenne, de a lapozast is meg kell valósítani valahogy, ahhoz pedig tudnom kene az user altal szerkesztheto tartalak szamat [ill. meg valamilyen mutatot is, ami kb kb tudja, hogy az pl. 5. oldalon honnan kezdje lekerdezgetni a tartalmakat.
A masik vazolt otletem, hogy az egesz ACL adatbazison belul van, amit sokkal nehezebb implementalni, elonye viszont, hogy nem passzolgat az alkalamazas es a db egymas kozott amig osszegyulik a peldai 10 szerkesztheto tartalom, hanem minden db-n belul fut le, ami gyorsabb es hatekonyabb.

Hát igen, space-time tradeoff :)

Egyik megoldás az lenne, hogy contentid-hez egy kapcsolótábla, ami minden userid-hez megmondja, hogy oké-e. (mármint ha van kapcsolat, akkor láthatja, ha nem, akkor nem) Ez akkor update-elődik, ha új contentid vagy új userid jön létre, az ACL alapján kiszámolódik, hogy ki mit, s beszúródnak a kívánt sorok.
Lekérdezésnél inner join, szűrve az userid-re.
Előnye, hogy egy joinnal megtörténik az ACL érvényesítése, semmi függvény-a-where-ben. Mivel sima lekérdezés lesz, simán tudsz paginálni, meg count-ozni.
Hátránya, hogy karban kell tartani, s minden egyes tartalombeszúrás/törlés lassabb lesz az ACL tábla frissítése miatt. Továbbá ha egy usert felveszel/törölsz/áthelyezel, az egész ACL táblát frissíteni kell (régi jogokat megvonni, újakat megadni). De ehhez lehet ügyes functiont írni (ami 80%-ban meg fog egyezni a 2. megoldás függvényével).
Ezt javaslom, ha tipikusan megjelenítesz.

Másik megoldás, hogy az ACL-t valahogy táblába rendezed, s írsz egy stored functiont, ami userid, contentid párosokat vár, s az acl-t végigkérdezi.
Ekkor a lekérdezés úgy történne, hogy a where-ben lesz egy AND HasRights(4032, contentid) = 1
Előnye, hogy hogy csak szabályokat tárolsz, és a beszúrás/törlés is elemi művelet.
Hátránya, hogy minden egyes sorra le kell futnia a functionnek. Rohadt sok időt fog enni egy count, mert ez nem indexelhető.

Én kevés adatra (max ezer rekord) a másodikat mondanám, egyéb esetben az első. Munka van vele, de ez van :)

Nahh, én már ott tartottam, hogy nem elég egy adott tartalom + adott user + adott művelet kapcsolatot vizsgálni, hanem a tartalom szülőinek műveleteit is nézni kell.Hisz hiába van szerkesztési joga a usernek egy tartalomhoz, ha a szülő tartalomnál le van tiltva a listázás számára, akkor nem listázhatom neki. És akkor itt már tényleg beláthatatlan távlatok nyílnak, mert lehetne az, hogy szülő valamilyen szabálya a gyerek tartalom egyik tulajdonságával kapcsolatos müveletet határozza meg (pl. csak a cím jelenhet meg listázásnál a lead nem). De a mai meetingen lényegesen leegyszerűsödtek a szabályok - szerencsére. ACL szabályok ésszerű keretek között tartása és értelmes formában történő megfogalmazása a megvalósíthatóság kulcsa.

Most 164 olvasásnál tart a topik és csak egyetlen reakció történt. Ez elég rossz aránynak tűnik. Nem tudom, hogy ez azért van, mert nem érthető amit kérdezek, vagy nincs rá ötlet?

Jól értem-e, ha sikerülne egy queryben mindent listázni, ami megfelel az ACL-eknek, akkor az egy elégséges megoldás? Azért én mégiscsak megpróbálnám SQL-be tenni az ACL-nek megfelelő kifejezést. Mondj egy olyan konkrét példát, amit szerinted nem lehet, hátha akkor többet tudunk segíteni.

--
joco voltam szevasz

Igen az elégséges megoldás lenne. A lehetséges szabályok száma ill. bonyolultsága riaszt el ettől. Brutálisan bonyolult query születne - ha egyáltalán kivitelezhető. Ha arra gondolok, hogy a szabályok egy része "megengedő" [mint a dátum-os az előző részben] illetve más részük "tiltó" [mint a parent csoport tagsága].. hát szép számú OR és AND feltétel lenne mindenféle JOIN-okkal.

Amíg nincs rekurzió meg egyéb okosságok, az ilyen szabályokat könnyen le lehet fordítani sql kifejezéssé. Le kell fordítani egyesével az elemi kifejezéseket, aztán egymás mellé tenni egy dinamikus queryben, közéjük tenni and/or/not kapcsolatot és ennyi.

--
joco voltam szevasz

Gondolkozom életszerű példán... Amíg olyan tulajdonság van a feltételek közt ami szerepel a tartalom táblájában vagy contentId alapján egy sorban egy másik táblában addig talán kivitelezhető, de pl ha mondjuk a kategória hierechiában lévő valamely szinten lévő kategória [amibe/amikbe a tartalom tartozik] tulajdonsága alapján van szabály akkor ott esetleg sub selectekkel v. többszörös joinokkal lehet már csak operálni. Minden esetre köszönöm az ötleted, rávezetett, hogy nem feltétlenül kell ezt alapból elvetni - mégha "fázom" is tőle kissé.

Ez alapesetben nem csak megoldható, hanem még szép feladat is. ACL->SQL kódgenerátor kell, ha értelmes formában vannak tárolva az ACL-ek, akkor triviális az egész.

A rekurzióval már trükközni kell. Egyes RDBMS-ekben van rá beépített SQL megoldás. Vagy pl. triggerekkel karbantarthatsz egy táblát, 2 oszlop, szülő és gyerek kulcs, ahol az összes közvetlen és közvetett szülő-gyerek kapcsolat megvan.

Így nézne ki pl. az SQL váz:

"SELECT * FROM Tartalom t WHERE (0=0 " + [megengedő kifejezések, amik t-re hivatkoznak, OR-ral elválasztva] + ") AND NOT (0=0 " + [tiltó kifejezések, amik t-re hivatkoznak, OR-ral elválasztva] + ")"

Egy egyszerű példa:
ACL: a szerző Lajos
SQL: t.Szerzo = 'Lajos'

Rekurziós elemi kifejezés pl. a fent említett szülő-gyerek táblával, korrelált subqueryvel:
ACL: ha valamelyik szülő elem szerzője Lajos
SQL: EXISTS (SELECT 1 FROM SzuloGyerek szgy JOIN Szulo sz ON sz.Id = szgy.SzuloId WHERE szgy.GyerekId = t.Id AND sz.Szerzo = 'Lajos')

--
joco voltam szevasz

Csakhogy a dolog bonyolultabb. A 'megengedő kifejezések' v. épp a 'tiltó kifejezések' kondicionálisan függnek egymástól és ez függés csak a db szinten kezelhető
pl.:

a.) Ha a bejelentkezett felhasználó a szerző minden további feltétel nélkül ok.
b.) Ha a felhasználó be van jelentkezve - de nem ő a szerző- akkor vizsgálni kell, h 1.) a tartalom megjelkenhet-e [status] és 2.) a tartalom megjelenésének időpontja korábbi -e az aktuális időpontnál.

SELECT SQL_CALC_FOUND_ROWS *
FROM content
WHERE `type` IN ('news','event','file')
AND
(
user_id = 41
OR (
41 > 0 AND `status`='1' AND come_out < '2012-09-14 02:01:25'
)
)
ORDER BY created_time DESC
LIMIT 10 OFFSET 0

Ezért szabályoktól függően egy követethetetlenül bonyolult query-t kéne felépíteni.

De nem kell követni a queryt. Lefordítod az elemi kifejezéseket, utána megfelelő logikai operátorokkal egymás mellé teszed a darabokat, és ennyi. Legyen az sql szerver dolga értelmezni.

Vagy az acl-ek nem dinamikusak? Akkor is jó ugyanez, csak nem kell automatizált sql kódgenerátort írni.

--
joco voltam szevasz

pl. debugnál lehet szempont, hogy mennyire bonyolult a query. HIába forditom le az elemi kifejezéseket, mert azok _kondicionálisan_egymástól_függenek. Ez alatt azt értem, hogy más-más elemi kifejezés csoportok szerint kell kiválasztódnia egy-egy tartalomnak függően egyik v. másik elemi kifejezéstől. De ezt az alkalmazás szintjén nem tudom eldönteni csak a db-ben. [Az alkalmazásban nem tudom, hogy ki a tartalom szerzője, így a db-ben kell vizsgálnom, hogy megegyezik -e a bejelentkezett felhasználóval, és ezen vizsgálat után tudom eldönteni hogy milyen további elemi kifejezéseket használok.] Vagyis nem tudom előre az alkalmazásban meghatározni azon feltételek körét amik szerint majd kiválasztódik v. nem az adott tartalom. Ez csak a db-ben dől el, ahol nem csak arról születik döntés, hogy megfelel v. sem egy-egy feltételnek, hanem arról is mely feltételeket kell figyelembe vennie és miket nem. Emiatt válik kezelhetetlenül bonyolultá.

Pedig egyszerű logika. Pl. ha ez van, az 1-es lehet a szerző vizsgálat, a 2-es a valódi kiválasztó feltétel, 3 és 4 egy másik páros:
"ha feltétel1 akkor feltétel2, ha feltétel3 akkor feltétel4"
Ez boolean logikával:
"(feltétel1 and feltétel2) or (feltétel3 and feltétel4)"
Tehát a feltételes feltétel is csak egy feltétel.

--
joco voltam szevasz

Az is eszembe jutott, hogy idonkent [naponta] az összes tartalom összes user összes müvelet relációban a jogosultságokat. Nyilván gigantikus táblává fog idővel nőni, de mindig csak userId, contentId, művelet kulcsokkal lesz használva. Persze új user, tartalomnál ill változtatásnál azt részben le és újra kell generálni, de nem az egészet.

hát ha olyan kapcsolatot hozol létre, hogy
{userid INT NOT NULL, contentid INT NOT NULL, action ENUM(VIEW, EDIT, DELETE) NOT NULL} (pfujj! enum!!!)
akkor az fixed length tábla lesz (nincs var* vagy blob oszlop).
Lássuk csak:

row length = 1
+ (sum of column lengths)
+ (number of NULL columns + delete_flag + 7)/8
+ (number of variable-length columns)

egy rekord 1+(4+4+1)+(0+1+7)/8 + (0)= 11 bájtot fog enni.
BIGINT id-kkel 19 Bájtot.

ez egymillió sorral 11 megás lesz köbö + ugyanekkora*1.5 index/oszlop.

Én elgondolkoznék db helyett xapian-on... Egyszerűbben kivitelezhető lenne és nem mellékesen gyorsabb is, de rekurzió esetén már más a helyzet
--
Kis problémából egy kis munkával nagy problémát lehet gyártani. Ha valami müxik ne b***tasd :)
Uriember az, aki nem beszél a Windows-ról, pedig tudna...

Koszi! Nem ismerem a Xapian-t, de tanulmányozni fogom. (http://getting-started-with-xapian.readthedocs.org/en/latest/howtos/que… - nem túl beszédes a doksi). Amúgy egy db csere nem opció jelenleg - még ha igény lenne rá (ahogy a vicc szól) - akkor sem lenne most rá idő meg energia.