
CREATE OR REPLACE FUNCTION checkACL(int,int) RETURNS boolean AS $$
	DECLARE
		pObjectID ALIAS FOR $1;
		pTrusteeID ALIAS FOR $2;
		groupid int;
	BEGIN
		-- Check for explicit access of the trustee to the object
		IF (SELECT count(*) FROM Security WHERE Object = pObjectID AND Trustee = pTrusteeID) > 0 THEN
			RETURN true;
		END IF;
		
		-- If the trustee is a user
		IF (SELECT IsGroup FROM Trustee WHERE ID = pTrusteeID) IS false THEN
			
			-- Iterate over all its person groups and recursively call checkACL
			FOR groupid IN (SELECT pgroup FROM membership WHERE Person = pTrusteeID) LOOP
				IF (SELECT checkACL(pObjectID, groupid)) IS true THEN
					RETURN true;
				END IF;
			END LOOP;
			
			-- Tutors always have access to all the data
			IF (SELECT bool_or(pgroup.tutors) FROM membership,pgroup WHERE membership.Person = pTrusteeID AND membership.pgroup = pgroup.ID) IS true THEN
				RETURN true;
			END IF;
		
		END IF;
		
		RETURN false;
	END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION checkACL(int) RETURNS boolean AS $$
	DECLARE
		userid int;
		res boolean;
	BEGIN
		SELECT INTO userid current_setting('unil.user')::integer;
		SELECT INTO res checkACL($1,userid);
		RETURN res;
	END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION applyACL(int, int[], int[]) RETURNS void AS $$
	DECLARE
		pObjectID ALIAS FOR $1;
		pGroupACL ALIAS FOR $2;
		pUserACL ALIAS FOR $3;
	BEGIN
		
		IF pGroupACL IS NOT NULL THEN
			
			DELETE FROM Security WHERE Object = pObjectID AND Trustee IN (SELECT ID FROM pgroup);
		
			IF array_lower(pGroupACL,1) IS NOT NULL THEN
				FOR i IN array_lower(pGroupACL,1)..array_upper(pGroupACL,1) LOOP
					IF (SELECT Tutor FROM pgroup WHERE ID = pGroupACL[i]) IS false THEN
						INSERT INTO Security(Object,Trustee) VALUES
							(pObjectID, pGroupACL[i]);
					END IF;
				END LOOP;
			END IF;
		END IF;

		IF pUserACL IS NOT NULL THEN

			DELETE FROM Security WHERE Object = pObjectID AND Trustee IN (SELECT ID FROM Person);

			IF array_lower(pUserACL,1) IS NOT NULL THEN
				FOR i IN array_lower(pUserACL,1)..array_upper(pUserACL,1) LOOP
					INSERT INTO Security(Object,Trustee) VALUES
						(pObjectID, pUserACL[i]);
				END LOOP;
			END IF;
		END IF;

	END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION assertACL(pObjectID int) RETURNS void AS $$
	BEGIN
		IF (NOT checkACL(pObjectID)) THEN
			RAISE EXCEPTION 'Access denied (ID=%)', pObjectID;
		END IF;
	END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION assertACL(pObjectID int,pTrusteeID int) RETURNS void AS $$
	BEGIN
		IF (NOT checkACL(pObjectID,pTrusteeID)) THEN
			RAISE EXCEPTION 'Access denied (ID=%)', pObjectID;
		END IF;
	END;
$$ LANGUAGE plpgsql;
