*/ error_reporting (E_ALL); require_once ("domframework/dblayer.php"); require_once ("domframework/authorization.php"); /** All the needed functions to authorize or deny access to an authenticated user */ class authorizationdb extends authorization { /** Separator between differents modules/objects */ private $separator = "/"; /** Database PDO connector */ private $db = null; /** Establish a connexion to the authorization database @param string $dsn The DSN needed to connect to database @param string|null $username The username needed to connect to database @param string|null $password The password needed to connect to database @param string|null $driver_options The driver options need to connect to database */ public function connectdb ($dsn, $username=null, $password=null, $driver_options=null) { // Define the structure of the table in the database $this->db = new dblayer ($dsn, $username, $password, $driver_options); $this->db->table = "domframework_authorization"; $this->db->fields = array ( "object"=>array ("varchar", "255", "not null"), "ownerid"=>array ("integer", "not null"), "groupid"=>array ("integer", "not null"), "modbits"=>array ("integer", "not null")); $this->db->primary = "object"; $this->db->unique = array ("object"); } /** Create the structure to store the authorization database */ public function initialize () { if ($this->db === null) throw new Exception (_("Database to authorize is not connected"), 500); $tables = $this->db->listTables (); if (!in_array ($this->db->table, $tables)) { $this->db->createTable (); $first = array ("object"=>"/", "ownerid"=>0, "groupid"=>0, "modbits"=>755); $this->db->create ($first); } } /** Return if the user right is NONE, READ, WRITE, EXECUTE if the object doesn't exists, or is not readable, throw an exception @param string $object The object path to examine @return an array with READ, WRITE, EXECUTE */ public function validate ($object) { if ($this->db === null) throw new Exception (_("Database to authorize is not connected"), 500); if (substr ($object, -1) === "/") $object = substr ($object, 0, -1); if (substr ($object, 0, 1) !== "/") throw new Exception (_("Object don't start by slash"), 412); $object = preg_replace ("#//+#", "/", $object); if ($this->authiduser === "") throw new Exception (_("Not authenticated"), 401); try { $this->treecheckExecute ($object); } catch (Exception $e) { throw new Exception ($e->getMessage(), 401); } // All the folder structure is accessible. Check if the object already // exists $search = $this->db->read (array (array ("object", $object))); if (count ($search) === 0) throw new Exception (sprintf (_("Object %s doesn't exists"), $object), 404); $search = reset ($search); $ownerid = intval ($search["ownerid"]); $groupid = intval ($search["groupid"]); $modbits = octdec ($search["modbits"]); if ($this->authiduser === 0) return array ("READ","WRITE","EXECUTE"); $res = array (); if ($this->authiduser === $ownerid) { if (($modbits & 0100) === 0100) $res[] = "EXECUTE"; if (($modbits & 0200) === 0200) $res[] = "WRITE"; if (($modbits & 0400) === 0400) $res[] = "READ"; } foreach ($this->authgroups as $group) { if ($group !== $groupid) continue; if (($modbits & 0010) === 0010) $res[] = "EXECUTE"; if (($modbits & 0020) === 0020) $res[] = "WRITE"; if (($modbits & 0040) === 0040) $res[] = "READ"; } if (($modbits & 0001) === 0001) $res[] = "EXECUTE"; if (($modbits & 0002) === 0002) $res[] = "WRITE"; if (($modbits & 0004) === 0004) $res[] = "READ"; return array_unique ($res); } /** Add a new object, with ownerid and groupid, and mode bits @param string $object Object path to add @param integer $ownerid Owner ID of the object @param integer $groupid Group ID of the object @param integer $modbits Bits of authorization @return TRUE or an exception with the error message */ public function add ($object, $ownerid, $groupid, $modbits) { if ($this->db === null) throw new Exception (_("Database to authorize is not connected"), 500); // The modbits are stored in octal to be more readable $modbits = decoct ($modbits); if (substr ($object, -1) === "/") $object = substr ($object, 0, -1); if (substr ($object, 0, 1) !== "/") throw new Exception (_("Object don't start by slash"), 412); $object = preg_replace ("#//+#", "/", $object); if ($this->authiduser === "") throw new Exception (_("Not authenticated !!"), 401); if ($this->authiduser !== 0 && $this->authiduser !== $ownerid) throw new Exception (_("Can't create object not owned by myself"), 412); if ($this->authiduser !== 0 && !in_array ($groupid, $this->authgroups)) throw new Exception (_("Can't create object with not owned group"), 412); try { $this->treecheckExecute ($object); } catch (Exception $e) { throw new Exception ($e->getMessage(), 401); } // All the folder structure is accessible. Check if the object already // exists $search = $this->db->read (array (array ("object", $object))); if (count ($search)) throw new Exception (sprintf (_("Object %s already defined"), $object), 400); // All the folder structure is accessible. Check if we can add the file in // the last folder (Write modbit). Root can always write. if ($this->authiduser === 0) { $this->db->create (array ("object"=>$object, "ownerid"=>$ownerid, "groupid"=>$groupid, "modbits"=>$modbits)); return TRUE; } try { $this->treecheckWrite ($object); } catch (Exception $e) { throw new Exception ($e->getMessage(), 401); } $this->db->create (array ("object"=>$object, "ownerid"=>$ownerid, "groupid"=>$groupid, "modbits"=>$modbits)); return TRUE; } /** Remove the informations about an object and all its sub-objects @param string $object Object path to drop @return TRUE or an exception with the error message */ public function drop ($object) { if ($this->db === null) throw new Exception (_("Database to authorize is not connected"), 500); if (substr ($object, -1) === "/") $object = substr ($object, 0, -1); if (substr ($object, 0, 1) !== "/") throw new Exception (_("Object don't start by slash"), 412); $object = preg_replace ("#//+#", "/", $object); if ($this->authiduser === "") throw new Exception (_("Not authenticated"), 401); if ($object === "/") throw new Exception (_("The root can not be removed"), 412); try { $this->treecheckExecute ($object); } catch (Exception $e) { throw new Exception ($e->getMessage(), 401); } // All the folder structure is accessible. Check if the object already // exists $search = $this->db->read (array (array ("object", $object))); if (count ($search) === 0) throw new Exception (sprintf (_("Object %s doesn't exists"), $object), 400); // All the folder structure is accessible. Check if we can remove the file // in the last folder (Write modbit). Root can always write. if ($this->authiduser === 0) { $rc = $this->db->delete ($object); if ($rc > 1) throw new Exception (_("Removing more than one object"), 412); if ($rc == 0) throw new Exception (_("No object removed"), 412); $rc = $this->db->delete ("$object$this->separator%"); return TRUE; } try { $this->treecheckWrite ($object); } catch (Exception $e) { throw new Exception ($e->getMessage(), 401); } $rc = $this->db->delete ($object); if ($rc > 1) throw new Exception (_("Removing more than one object"), 412); if ($rc == 0) throw new Exception (_("No object removed"), 412); $rc = $this->db->delete ("$object$this->separator%"); return TRUE; } /** Change the owner of an object Need to be the root administrator @param string $object Object path to add @param integer $ownerid Owner ID of the object @return TRUE or an exception with the error message */ public function chown ($object, $ownerid) { if ($this->db === null) throw new Exception (_("Database to authorize is not connected"), 500); if (substr ($object, -1) === "/") $object = substr ($object, 0, -1); if (substr ($object, 0, 1) !== "/") throw new Exception (_("Object don't start by slash"), 412); $object = preg_replace ("#//+#", "/", $object); if ($this->authiduser === "") throw new Exception (_("Not authenticated !!"), 401); if ($this->authiduser !== 0) throw new Exception (_("The chown is reserved to root user"), 401); try { $this->treecheckExecute ($object); } catch (Exception $e) { throw new Exception ($e->getMessage(), 401); } // All the folder structure is accessible. Check if the object already // exists $search = $this->db->read (array (array ("object", $object))); if (count ($search) === 0) throw new Exception (sprintf (_("Object %s doesn't exists"), $object), 400); $search = reset ($search); $this->db->update ($object, array ("ownerid"=>$ownerid)); return TRUE; } /** Change the group of an object Need to be the ownerid of the object or the root administrator @param string $object Object path to add @param integer $groupid Group ID of the object @return TRUE or an exception with the error message */ public function chgrp ($object, $groupid) { if ($this->db === null) throw new Exception (_("Database to authorize is not connected"), 500); if (substr ($object, -1) === "/") $object = substr ($object, 0, -1); if (substr ($object, 0, 1) !== "/") throw new Exception (_("Object don't start by slash"), 412); $object = preg_replace ("#//+#", "/", $object); if ($this->authiduser === "") throw new Exception (_("Not authenticated"), 401); if ($this->authiduser !== 0 && !in_array ($groupid, $this->authgroups)) throw new Exception (_("The user must be in the wanted group"), 401); if (!in_array ("WRITE", $this->validate ($object))) throw new Exception (sprintf (_("%s is write protected"), $object), 401); try { $this->treecheckExecute ($object); } catch (Exception $e) { throw new Exception ($e->getMessage(), 401); } // All the folder structure is accessible. Check if the object already // exists $search = $this->db->read (array (array ("object", $object))); if (count ($search) === 0) throw new Exception (sprintf (_("Object %s doesn't exists"), $object), 400); $search = reset ($search); $this->db->update ($object, array ("groupid"=>$groupid)); return TRUE; } /** Change mode bits for an object Need to be the owner of the object or the root administrator @param string $object Object path to change @param integer $mod Bits of authorization @return TRUE or an exception with the error message */ public function chmod ($object, $mod) { if ($this->db === null) throw new Exception (_("Database to authorize is not connected"), 500); if (substr ($object, -1) === "/") $object = substr ($object, 0, -1); if (substr ($object, 0, 1) !== "/") throw new Exception (_("Object don't start by slash"), 412); $object = preg_replace ("#//+#", "/", $object); if ($this->authiduser === "") throw new Exception (_("Not authenticated"), 401); if (!in_array ("WRITE", $this->validate ($object))) throw new Exception (sprintf (_("%s is write protected"), $object), 401); try { $this->treecheckExecute ($object); } catch (Exception $e) { throw new Exception ($e->getMessage(), 401); } // All the folder structure is accessible. Check if the object already // exists $search = $this->db->read (array (array ("object", $object))); if (count ($search) === 0) throw new Exception (sprintf (_("Object %s doesn't exists"), $object), 400); $search = reset ($search); // TODO : Don't update if the user is not the owner of the file $this->db->update ($object, array ("modbits"=>$modbits)); return TRUE; } /** Return the mode bits for an object if all his parents are readable for the user @param string $object Object path to examine @return the modbits or an exception with the error message */ public function lsmod ($object) { if ($this->db === null) throw new Exception (_("Database to authorize is not connected"), 500); if (substr ($object, -1) === "/") $object = substr ($object, 0, -1); if (substr ($object, 0, 1) !== "/") throw new Exception (_("Object don't start by slash"), 412); $object = preg_replace ("#//+#", "/", $object); if ($this->authiduser === "") throw new Exception (_("Not authenticated"), 401); try { $this->treecheckExecute ($object); } catch (Exception $e) { throw new Exception ($e->getMessage(), 401); } // All the folder structure is accessible. Check if the object already // exists $search = $this->db->read (array (array ("object", $object))); if (count ($search) === 0) throw new Exception (sprintf (_("Object %s doesn't exists"), $object), 400); $search = reset ($search); return octdec ($search["modbits"]); } /** Return the ownerid for an object if all his parents are readable for the user @param string $object Object path to examine @return the ownerid or an exception with the error message */ public function lsown ($object) { if ($this->db === null) throw new Exception (_("Database to authorize is not connected"), 500); if (substr ($object, -1) === "/") $object = substr ($object, 0, -1); if (substr ($object, 0, 1) !== "/") throw new Exception (_("Object don't start by slash"), 412); $object = preg_replace ("#//+#", "/", $object); if ($this->authiduser === "") throw new Exception (_("Not authenticated !!"), 401); try { $this->treecheckExecute ($object); } catch (Exception $e) { throw new Exception ($e->getMessage(), 401); } // All the folder structure is accessible. Check if the object already // exists $search = $this->db->read (array (array ("object", $object))); if (count ($search) === 0) throw new Exception (sprintf (_("Object %s doesn't exists"), $object), 400); $search = reset ($search); return intval ($search["ownerid"]); } /** Return the groupid for an object if all his parents are readable for the user @param string $object Object path to examine @return the groupid or an exception with the error message */ public function lsgrp ($object) { if ($this->db === null) throw new Exception (_("Database to authorize is not connected"), 500); if (substr ($object, -1) === "/") $object = substr ($object, 0, -1); if (substr ($object, 0, 1) !== "/") throw new Exception (_("Object don't start by slash"), 412); $object = preg_replace ("#//+#", "/", $object); if ($this->authiduser === "") throw new Exception (_("Not authenticated !!"), 401); try { $this->treecheckExecute ($object); } catch (Exception $e) { throw new Exception ($e->getMessage(), 401); } // All the folder structure is accessible. Check if the object already // exists $search = $this->db->read (array (array ("object", $object))); if (count ($search) === 0) throw new Exception (sprintf (_("Object %s doesn't exists"), $object), 400); $search = reset ($search); return intval ($search["groupid"]); } //////////////////// // TREE CHECK // //////////////////// /** Check if all the parent objects are executable @param string $object The object to test @return TRUE or an exception in case of error */ private function treecheckExecute ($object) { if ($this->db === null) throw new Exception (_("Database to authorize is not connected"), 500); // Search all the parents in an array $parents = array (); $par = $object; while ($par !== "/") { $par = dirname ($par); $parents[] = $par; } // For each parent (folder), check if there is the right to cross (Execute // modbit). Look at the database in one read request for all the parents. $parents = array_reverse ($parents); $req = "SELECT object,ownerid,groupid,modbits FROM domframework_authorization WHERE "; foreach ($parents as $i=>$p) { if ($i > 0) $req .= " OR "; $req .= "object='".addslashes ($p)."'"; } $res = $this->db->directRead ($req); foreach ($parents as $i=>$p) { $found = false; foreach ($res as $r) { if ($r["object"] === $p) { $found = true; break; } } if (!$found) throw new Exception (sprintf (_("The path %s is not found in database"), $p), 404); else { $parentOwner = intval ($r["ownerid"]); $parentGroup = intval ($r["groupid"]); $parentModbits = octdec ($r["modbits"]); if ($this->authiduser === 0) continue; if ((($parentModbits & 0100) === 64) && $parentOwner === $this->authiduser) continue; if ((($parentModbits & 0010) === 8)) { foreach ($this->authgroups as $tmpgrp) { if ($parentGroup === $tmpgrp || $tmpgrp === 0) continue 2; } } if (($parentModbits & 0001) === 1) continue; throw new Exception (sprintf (_("No execute rights on %s"), $p), 401); } } return TRUE; } /** Check if the parent of object is writeable @param string $object The object to found @return TRUE or an exception */ private function treecheckWrite ($object) { $parent = dirname ($object); $search = $this->db->read (array (array ("object", $parent))); $search = reset ($search); $parentOwner = intval ($search["ownerid"]); $parentGroup = intval ($search["groupid"]); $parentModbits = octdec ($search["modbits"]); if ((($parentModbits & 0200) === 128) && $parentOwner === $this->authiduser) return TRUE; if ((($parentModbits & 0020) === 16)) { if (in_array ($parentGroup, $this->authgroups) || in_array (0, $this->authgroups)) return TRUE; } if (($parentModbits & 0002) === 2) return TRUE; throw new Exception (sprintf (_("No write rights on %s"), $parent), 401); } }