git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@2513 bf3deb0d-5f1a-0410-827f-c0cc1f45334c
631 lines
22 KiB
PHP
631 lines
22 KiB
PHP
<?php
|
|
/** DomFramework
|
|
@package domframework
|
|
@author Dominique Fournier <dominique@fournier38.fr> */
|
|
|
|
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;
|
|
/** The prefix for the table */
|
|
public $tableprefix = "";
|
|
|
|
/** 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->tableprefix = $this->tableprefix;
|
|
$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 (dgettext("domframework",
|
|
"Database to authorize is not connected"), 500);
|
|
$tables = $this->db->listTables ();
|
|
if (!in_array ($this->db->tableprefix.$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 (dgettext("domframework",
|
|
"Database to authorize is not connected"), 500);
|
|
if (substr ($object, -1) === "/")
|
|
$object = substr ($object, 0, -1);
|
|
if (substr ($object, 0, 1) !== "/")
|
|
throw new Exception (dgettext("domframework",
|
|
"Object don't start by slash"), 406);
|
|
$object = preg_replace ("#//+#", "/", $object);
|
|
if ($this->authiduser === "")
|
|
throw new Exception (dgettext("domframework",
|
|
"Not authenticated"), 401);
|
|
try
|
|
{
|
|
$this->treecheckExecute ($object);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
throw new Exception ($e->getMessage(), 405);
|
|
}
|
|
|
|
// 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 (dgettext("domframework",
|
|
"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 (dgettext("domframework",
|
|
"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 (dgettext("domframework",
|
|
"Object don't start by slash"), 406);
|
|
$object = preg_replace ("#//+#", "/", $object);
|
|
if ($this->authiduser === "")
|
|
throw new Exception (dgettext("domframework",
|
|
"Not authenticated"), 401);
|
|
if ($this->authiduser !== 0 && $this->authiduser !== $ownerid)
|
|
throw new Exception (dgettext("domframework",
|
|
"Can't create object not owned by myself"), 406);
|
|
if ($this->authiduser !== 0 && !in_array ($groupid, $this->authgroups))
|
|
throw new Exception (dgettext("domframework",
|
|
"Can't create object with not owned group"), 406);
|
|
try
|
|
{
|
|
$this->treecheckExecute ($object);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
throw new Exception ($e->getMessage(), 405);
|
|
}
|
|
|
|
// 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 (dgettext("domframework",
|
|
"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(), 405);
|
|
}
|
|
|
|
$this->db->create (array ("object"=>$object,
|
|
"ownerid"=>$ownerid,
|
|
"groupid"=>$groupid,
|
|
"modbits"=>$modbits));
|
|
return TRUE;
|
|
}
|
|
|
|
/** Remove the information 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 (dgettext("domframework",
|
|
"Database to authorize is not connected"), 500);
|
|
if (substr ($object, -1) === "/")
|
|
$object = substr ($object, 0, -1);
|
|
if (substr ($object, 0, 1) !== "/")
|
|
throw new Exception (dgettext("domframework",
|
|
"Object don't start by slash"), 406);
|
|
$object = preg_replace ("#//+#", "/", $object);
|
|
if ($this->authiduser === "")
|
|
throw new Exception (dgettext("domframework",
|
|
"Not authenticated"), 401);
|
|
if ($object === "/")
|
|
throw new Exception (dgettext("domframework",
|
|
"The root can not be removed"), 406);
|
|
try
|
|
{
|
|
$this->treecheckExecute ($object);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
throw new Exception ($e->getMessage(), 405);
|
|
}
|
|
|
|
// 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 (dgettext("domframework",
|
|
"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 (dgettext("domframework",
|
|
"Removing more than one object"), 406);
|
|
if ($rc == 0)
|
|
throw new Exception (dgettext("domframework",
|
|
"No object removed"), 406);
|
|
$rc = $this->db->delete ("$object$this->separator%");
|
|
return TRUE;
|
|
}
|
|
|
|
try
|
|
{
|
|
$this->treecheckWrite ($object);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
throw new Exception ($e->getMessage(), 405);
|
|
}
|
|
|
|
$rc = $this->db->delete ($object);
|
|
if ($rc > 1)
|
|
throw new Exception (dgettext("domframework",
|
|
"Removing more than one object"), 406);
|
|
if ($rc == 0)
|
|
throw new Exception (dgettext("domframework",
|
|
"No object removed"), 406);
|
|
$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 (dgettext("domframework",
|
|
"Database to authorize is not connected"), 500);
|
|
if (substr ($object, -1) === "/")
|
|
$object = substr ($object, 0, -1);
|
|
if (substr ($object, 0, 1) !== "/")
|
|
throw new Exception (dgettext("domframework",
|
|
"Object don't start by slash"), 406);
|
|
$object = preg_replace ("#//+#", "/", $object);
|
|
if ($this->authiduser === "")
|
|
throw new Exception (dgettext("domframework",
|
|
"Not authenticated"), 401);
|
|
if ($this->authiduser !== 0)
|
|
throw new Exception (dgettext("domframework",
|
|
"The chown is reserved to root user"), 405);
|
|
try
|
|
{
|
|
$this->treecheckExecute ($object);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
throw new Exception ($e->getMessage(), 405);
|
|
}
|
|
|
|
// 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 (dgettext("domframework",
|
|
"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 (dgettext("domframework",
|
|
"Database to authorize is not connected"), 500);
|
|
if (substr ($object, -1) === "/")
|
|
$object = substr ($object, 0, -1);
|
|
if (substr ($object, 0, 1) !== "/")
|
|
throw new Exception (dgettext("domframework",
|
|
"Object don't start by slash"), 406);
|
|
$object = preg_replace ("#//+#", "/", $object);
|
|
if ($this->authiduser === "")
|
|
throw new Exception (dgettext("domframework",
|
|
"Not authenticated"), 401);
|
|
if ($this->authiduser !== 0 && !in_array ($groupid, $this->authgroups))
|
|
throw new Exception (dgettext("domframework",
|
|
"The user must be in the wanted group"), 405);
|
|
if (!in_array ("WRITE", $this->validate ($object)))
|
|
throw new Exception (sprintf (dgettext("domframework",
|
|
"%s is write protected"), $object), 405);
|
|
try
|
|
{
|
|
$this->treecheckExecute ($object);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
throw new Exception ($e->getMessage(), 405);
|
|
}
|
|
|
|
// 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 (dgettext("domframework",
|
|
"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 (dgettext("domframework",
|
|
"Database to authorize is not connected"), 500);
|
|
if (substr ($object, -1) === "/")
|
|
$object = substr ($object, 0, -1);
|
|
if (substr ($object, 0, 1) !== "/")
|
|
throw new Exception (dgettext("domframework",
|
|
"Object don't start by slash"), 406);
|
|
$object = preg_replace ("#//+#", "/", $object);
|
|
if ($this->authiduser === "")
|
|
throw new Exception (dgettext("domframework",
|
|
"Not authenticated"), 401);
|
|
if (!in_array ("WRITE", $this->validate ($object)))
|
|
throw new Exception (sprintf (dgettext("domframework",
|
|
"%s is write protected"), $object), 405);
|
|
try
|
|
{
|
|
$this->treecheckExecute ($object);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
throw new Exception ($e->getMessage(), 405);
|
|
}
|
|
|
|
// 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 (dgettext("domframework",
|
|
"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 (dgettext("domframework",
|
|
"Database to authorize is not connected"), 500);
|
|
if (substr ($object, -1) === "/")
|
|
$object = substr ($object, 0, -1);
|
|
if (substr ($object, 0, 1) !== "/")
|
|
throw new Exception (dgettext("domframework",
|
|
"Object don't start by slash"), 406);
|
|
$object = preg_replace ("#//+#", "/", $object);
|
|
if ($this->authiduser === "")
|
|
throw new Exception (dgettext("domframework",
|
|
"Not authenticated"), 401);
|
|
try
|
|
{
|
|
$this->treecheckExecute ($object);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
throw new Exception ($e->getMessage(), 405);
|
|
}
|
|
|
|
// 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 (dgettext("domframework",
|
|
"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 (dgettext("domframework",
|
|
"Database to authorize is not connected"), 500);
|
|
if (substr ($object, -1) === "/")
|
|
$object = substr ($object, 0, -1);
|
|
if (substr ($object, 0, 1) !== "/")
|
|
throw new Exception (dgettext("domframework",
|
|
"Object don't start by slash"), 406);
|
|
$object = preg_replace ("#//+#", "/", $object);
|
|
if ($this->authiduser === "")
|
|
throw new Exception (dgettext("domframework",
|
|
"Not authenticated"), 401);
|
|
try
|
|
{
|
|
$this->treecheckExecute ($object);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
throw new Exception ($e->getMessage(), 405);
|
|
}
|
|
|
|
// 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 (dgettext("domframework",
|
|
"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 (dgettext("domframework",
|
|
"Database to authorize is not connected"), 500);
|
|
if (substr ($object, -1) === "/")
|
|
$object = substr ($object, 0, -1);
|
|
if (substr ($object, 0, 1) !== "/")
|
|
throw new Exception (dgettext("domframework",
|
|
"Object don't start by slash"), 406);
|
|
$object = preg_replace ("#//+#", "/", $object);
|
|
if ($this->authiduser === "")
|
|
throw new Exception (dgettext("domframework",
|
|
"Not authenticated"), 401);
|
|
try
|
|
{
|
|
$this->treecheckExecute ($object);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
throw new Exception ($e->getMessage(), 405);
|
|
}
|
|
|
|
// 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 (dgettext("domframework",
|
|
"Object %s doesn't exists"), $object),
|
|
404);
|
|
$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 (dgettext("domframework",
|
|
"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 (dgettext("domframework",
|
|
"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 (dgettext("domframework",
|
|
"No execute rights on %s"), $p), 405);
|
|
}
|
|
}
|
|
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 (dgettext("domframework",
|
|
"No write rights on %s"), $parent), 405);
|
|
}
|
|
}
|