libretime/backend/Subjects.php

683 lines
19 KiB
PHP

<?php
define('ALIBERR_NOTGR', 20);
define('ALIBERR_BADSMEMB', 21);
/**
* Subj class
*
* users + groups
* with "linearized recursive membership" ;)
* (allow adding users to groups or groups to groups)
*
* @package Campcaster
* @subpackage Alib
* @copyright 2010 Sourcefabric O.P.S.
* @license http://www.gnu.org/licenses/gpl.txt
* @see ObjClasses
* @see Alib
*/
class Subjects {
/* ======================================================= public methods */
/**
* Add new subject (a.k.a. "user")
*
* @param string $p_login
* @param string $p_pass
* @param string $p_realname
* @param boolean $p_passenc
* password already encrypted if true
* @return int|PEAR_Error
*/
public static function AddSubj($p_login, $p_pass=NULL, $p_realname='', $p_passenc=FALSE)
{
global $CC_CONFIG, $CC_DBC;
if (!$p_login) {
return $CC_DBC->raiseError("Subjects::AddSubj: empty login");
}
$id = $CC_DBC->nextId($CC_CONFIG['subjSequence']);
if (PEAR::isError($id)) {
return $id;
}
if (!is_null($p_pass) && !$p_passenc) {
$p_pass = md5($p_pass);
}
$sql = "INSERT INTO ".$CC_CONFIG['subjTable']." (id, login, pass, type, realname)"
." VALUES ($id, '$p_login', ".
(is_null($p_pass) ? "'!', 'G'" : "'$p_pass', 'U'").",
'$p_realname')";
$r = $CC_DBC->query($sql);
if (PEAR::isError($r)) {
return $r;
}
return $id;
}
/**
* Remove subject by uid or by login
*
* @param string $login
* @param int $uid
* @return boolean|PEAR_Error
*/
public static function RemoveSubj($login, $uid=NULL)
{
global $CC_CONFIG, $CC_DBC;
if (is_null($uid)) {
$uid = Subjects::GetSubjId($login);
}
if (PEAR::isError($uid)) {
return $uid;
}
$sql = "DELETE FROM ".$CC_CONFIG['smembTable']
." WHERE (uid='$uid' OR gid='$uid') AND mid is null";
$r = $CC_DBC->query($sql);
if (PEAR::isError($r)) {
return $r;
}
$sql2 = "DELETE FROM ".$CC_CONFIG['subjTable']
." WHERE login='$login'";
$r = $CC_DBC->query($sql2);
if (PEAR::isError($r)) {
return $r;
}
return Subjects::_rebuildRels();
} // fn removeSubj
/**
* Check login and password
*
* @param string $login
* @param string $pass
* optional
* @return boolean|int|PEAR_Error
*/
public static function Authenticate($login, $pass='')
{
global $CC_CONFIG, $CC_DBC;
$cpass = md5($pass);
$sql = "SELECT id FROM ".$CC_CONFIG['subjTable']
." WHERE login='$login' AND pass='$cpass' AND type='U'";
$id = $CC_DBC->getOne($sql);
if (PEAR::isError($id)) {
return $id;
}
return (is_null($id) ? FALSE : $id);
} // fn authenticate
/**
* Set lastlogin or lastfail timestamp
*
* @param string $login
* @param boolean $failed
* true=> set lastfail, false=> set lastlogin
* @return boolean|int|PEAR_Error
*/
public static function SetTimeStamp($login, $failed=FALSE)
{
global $CC_CONFIG, $CC_DBC;
$fld = ($failed ? 'lastfail' : 'lastlogin');
$sql = "UPDATE ".$CC_CONFIG['subjTable']." SET $fld=now()"
." WHERE login='$login'";
$r = $CC_DBC->query($sql);
if (PEAR::isError($r)) {
return $r;
}
return TRUE;
} // fn setTimeStamp
/**
* Change user password
*
* @param string $login
* @param string $oldpass
* old password (optional for 'superuser mode')
* @param string $pass
* optional
* @param boolean $passenc
* optional, password already encrypted if true
* @return boolean|PEAR_Error
*/
public static function Passwd($login, $oldpass=null, $pass='', $passenc=FALSE)
{
global $CC_CONFIG, $CC_DBC;
if (!$passenc) {
$cpass = md5($pass);
} else {
$cpass = $pass;
}
if (!is_null($oldpass)) {
$oldcpass = md5($oldpass);
$oldpCond = "AND pass='$oldcpass'";
} else {
$oldpCond = '';
}
$sql = "UPDATE ".$CC_CONFIG['subjTable']." SET pass='$cpass'"
." WHERE login='$login' $oldpCond AND type='U'";
$r = $CC_DBC->query($sql);
if (PEAR::isError($r)) {
return $r;
}
return TRUE;
} // fn passwd
/* --------------------------------------------------------------- groups */
/**
* Add {login} and direct/indirect members to {gname} and to groups,
* where {gname} is [in]direct member
*
* @param string $login
* @param string $gname
* @return int|PEAR_Error
*/
public static function AddSubjectToGroup($login, $gname)
{
$uid = Subjects::GetSubjId($login);
if (PEAR::isError($uid)) {
return $uid;
}
$gid = Subjects::GetSubjId($gname);
if (PEAR::isError($gid)) {
return $gid;
}
$isgr = Subjects::IsGroup($gid);
if (PEAR::isError($isgr)) {
return $isgr;
}
if (!$isgr) {
return PEAR::raiseError("Subjects::addSubj2Gr: Not a group ($gname)", ALIBERR_NOTGR);
}
// add subject and all [in]direct members to group $gname:
$mid = Subjects::_plainAddSubjectToGroup($uid, $gid);
if (PEAR::isError($mid)) {
return $mid;
}
// add it to all groups where $gname is [in]direct member:
$marr = Subjects::_listRMemb($gid);
if (PEAR::isError($marr)) {
return $marr;
}
foreach ($marr as $k => $v) {
$r = Subjects::_plainAddSubjectToGroup(
$uid, $v['gid'], intval($v['level'])+1, $v['id']);
if (PEAR::isError($r)) {
return $r;
}
}
return $mid;
} // fn addSubj2Gr
/**
* Remove subject from group
*
* @param string $login
* @param string $gname
* @return boolean|PEAR_Error
*/
public static function RemoveSubjectFromGroup($login, $gname)
{
global $CC_CONFIG, $CC_DBC;
$uid = Subjects::GetSubjId($login);
if (PEAR::isError($uid)) {
return $uid;
}
$gid = Subjects::GetSubjId($gname);
if (PEAR::isError($gid)) {
return $gid;
}
$sql = "SELECT id FROM ".$CC_CONFIG['smembTable']
." WHERE uid='$uid' AND gid='$gid' AND mid is null";
$mid = $CC_DBC->getOne($sql);
if (is_null($mid)) {
return FALSE;
}
if (PEAR::isError($mid)) {
return $mid;
}
// remove it:
$r = Subjects::_removeMemb($mid);
if (PEAR::isError($r)) {
return $r;
}
// and rebuild indirect memberships:
$r = Subjects::_rebuildRels();
if (PEAR::isError($r)) {
return $r;
}
return TRUE;
} // fn removeSubjFromGr
/* --------------------------------------------------------- info methods */
/**
* Get subject id from login
*
* @param string $login
* @return int|PEAR_Error
*/
public static function GetSubjId($login)
{
global $CC_CONFIG;
global $CC_DBC;
$sql = "SELECT id FROM ".$CC_CONFIG['subjTable']
." WHERE login='$login'";
return $CC_DBC->getOne($sql);
} // fn getSubjId
/**
* Get subject name (login) from id
*
* @param int $id
* @param string $fld
* @return string|PEAR_Error
*/
public static function GetSubjName($id, $fld='login')
{
global $CC_CONFIG;
global $CC_DBC;
$sql = "SELECT $fld FROM ".$CC_CONFIG['subjTable']
." WHERE id='$id'";
return $CC_DBC->getOne($sql);
} // fn getSubjName
/**
* Get one subject from the table.
*
* @param string $p_fieldValue
* @param string $p_fieldName
* @return array
*/
public static function GetSubject($p_fieldValue, $p_fieldName='login')
{
global $CC_CONFIG, $CC_DBC;
if (!in_array($p_fieldName, array("login", "id"))) {
return null;
}
$escapedValue = pg_escape_string($p_fieldValue);
$sql = "SELECT * FROM ".$CC_CONFIG['subjTable']
." WHERE $p_fieldName='$escapedValue'";
$row = $CC_DBC->GetRow($sql);
return $row;
}
/**
* Get all subjects
*
* @param string $flds
* @return array|PEAR_Error
*/
public static function GetSubjects($flds='id, login')
{
global $CC_CONFIG, $CC_DBC;
$sql = "SELECT $flds FROM ".$CC_CONFIG['subjTable'];
return $CC_DBC->getAll($sql);
} // fn getSubjects
/**
* Get subjects with count of direct members
*
* @return array|PEAR_Error
*/
public static function GetSubjectsWCnt()
{
global $CC_CONFIG, $CC_DBC;
$sql = "SELECT count(m.uid)as cnt, s.id, s.login, s.type"
." FROM ".$CC_CONFIG['subjTable']." s"
." LEFT JOIN ".$CC_CONFIG['smembTable']." m ON m.gid=s.id"
." WHERE m.mid is null"
." GROUP BY s.id, s.login, s.type"
." ORDER BY s.id";
return $CC_DBC->getAll($sql);
} // fn getSubjectsWCnt
/**
* Return true if subject is a group
*
* @param int $gid
* @return boolean|PEAR_Error
*/
public static function IsGroup($gid)
{
global $CC_CONFIG, $CC_DBC;
if (empty($gid)) {
return FALSE;
}
$sql = "SELECT type FROM ".$CC_CONFIG['subjTable']
." WHERE id='$gid'";
$r = $CC_DBC->getOne($sql);
if (PEAR::isError($r)) {
return $r;
}
return ($r === 'G');
} // fn isGroup
/**
* List direct members of group
*
* @param int $gid
* @return array|PEAR_Error
*/
public static function ListGroup($gid)
{
global $CC_CONFIG, $CC_DBC;
$sql = "SELECT s.id, s.login, s.type"
." FROM ".$CC_CONFIG['smembTable']." m, ".$CC_CONFIG['subjTable']." s"
." WHERE m.uid=s.id AND m.mid is null AND m.gid='$gid'";
return $CC_DBC->getAll($sql);
} // fn listGroup
/**
* Return true if uid is [id]direct member of gid
*
* @param int $uid
* local user id
* @param int $gid
* local group id
* @return boolean
*/
public static function IsMemberOf($uid, $gid)
{
global $CC_CONFIG, $CC_DBC;
$sql = "SELECT count(*)as cnt"
." FROM ".$CC_CONFIG['smembTable']
." WHERE uid='$uid' AND gid='$gid'";
$res = $CC_DBC->getOne($sql);
if (PEAR::isError($res)) {
return $res;
}
return (intval($res) > 0);
} // fn isMemberOf
/* ==================================================== "private" methods */
/**
* Create membership record
*
* @param int $uid
* @param int $gid
* @param int $level
* @param int $mid
* @return int|PEAR_Error
*/
private static function _addMemb($uid, $gid, $level=0, $mid='null')
{
global $CC_CONFIG, $CC_DBC;
if ($uid == $gid) {
return PEAR::raiseError("Subjects::_addMemb: uid==gid ($uid)", ALIBERR_BADSMEMB);
}
$sql = "SELECT id, level, mid FROM ".$CC_CONFIG['smembTable']
." WHERE uid='$uid' AND gid='$gid' ORDER BY level ASC";
$a = $CC_DBC->getAll($sql);
if (PEAR::isError($a)) {
return $a;
}
if (count($a) > 0) {
$a0 = $a[0];
$id = $a0['id'];
if ($level < intval($a0['level'])){
$sql2 = "UPDATE ".$CC_CONFIG['smembTable']
." SET level='$level', mid=$mid WHERE id='{$a0['id']}'";
$r = $CC_DBC->query($sql2);
if (PEAR::isError($r)) {
return $r;
}
}
} else {
$id = $CC_DBC->nextId($CC_CONFIG['smembSequence']);
if (PEAR::isError($id)) {
return $id;
}
$sql3 = "INSERT INTO ".$CC_CONFIG['smembTable']." (id, uid, gid, level, mid)"
." VALUES ($id, $uid, $gid, $level, $mid)";
$r = $CC_DBC->query($sql3);
if (PEAR::isError($r)) {
return $r;
}
}
return $id;
} // fn _addMemb
/**
* Remove membership record
*
* @param int $mid
* @return null|PEAR_Error
*/
private static function _removeMemb($mid)
{
global $CC_CONFIG, $CC_DBC;
$sql = "DELETE FROM ".$CC_CONFIG['smembTable']
." WHERE id='$mid'";
return $CC_DBC->query($sql);
} // fn _removeMemb
/**
* List [in]direct members of group
*
* @param int $gid
* @param int $uid
* @return array|PEAR_Error
*/
private static function _listMemb($gid, $uid=NULL)
{
global $CC_CONFIG, $CC_DBC;
$sql = "SELECT id, uid, level FROM ".$CC_CONFIG['smembTable']
." WHERE gid='$gid'".(is_null($uid) ? '' : " AND uid='$uid'");
return $CC_DBC->getAll($sql);
} // fn _listMemb
/**
* List groups where uid is [in]direct member
*
* @param int $gid
* @param int $uid
* @return array|PEAR_Error
*/
private static function _listRMemb($uid, $gid=NULL)
{
global $CC_CONFIG, $CC_DBC;
$sql = "SELECT id, gid, level FROM ".$CC_CONFIG['smembTable']
." WHERE uid='$uid'".(is_null($gid) ? '' : " AND gid='$gid'");
return $CC_DBC->getAll($sql);
} // fn listRMemb
/**
* Add uid and its [in]direct members to gid
*
* @param int $uid
* @param int $gid
* @param int $level
* @param int $rmid
* @return int|PEAR_Error
*/
private static function _plainAddSubjectToGroup($uid, $gid, $level=0, $rmid='null')
{
$mid = Subjects::_addMemb($uid, $gid, $level, $rmid);
if (PEAR::isError($mid)) {
return $mid;
}
$marr = Subjects::_listMemb($uid);
if (PEAR::isError($marr)) {
return $marr;
}
foreach ($marr as $k => $v) {
$r = Subjects::_addMemb(
$v['uid'], $gid, intval($v['level'])+$level+1, $mid
);
if (PEAR::isError($r)) {
return $r;
}
}
return $mid;
}
/**
* Rebuild indirect membership records<br>
* it's probably more complicated to do removing without rebuild ...
*
* @return true|PEAR_Error
*/
private static function _rebuildRels()
{
global $CC_CONFIG, $CC_DBC;
$CC_DBC->query("BEGIN");
$r = $CC_DBC->query("LOCK TABLE ".$CC_CONFIG['smembTable']);
if (PEAR::isError($r)) {
return $r;
}
$sql = "DELETE FROM ".$CC_CONFIG['smembTable']
." WHERE mid is not null";
$r = $CC_DBC->query($sql);
if (PEAR::isError($r)) {
return $r;
}
$arr = $CC_DBC->getAll("SELECT uid, gid FROM ".$CC_CONFIG['smembTable']);
// WHERE mid is null
if (PEAR::isError($arr)) {
return $arr;
}
foreach ($arr as $it) {
$marr = Subjects::_listRMemb($it['gid']);
if (PEAR::isError($marr)) {
return $marr;
}
foreach ($marr as $k => $v) {
$r = Subjects::_plainAddSubjectToGroup(
$it['uid'], $v['gid'], intval($v['level'])+1, $v['id']
);
if (PEAR::isError($r)) {
return $r;
}
}
}
$r = $CC_DBC->query("COMMIT");
if (PEAR::isError($r)) {
return $r;
}
return TRUE;
} // fn _rebuildRels
/* =============================================== test and debug methods */
/**
* Dump subjects for debug
*
* @param string $indstr
* indentation string
* @param string $ind
* actual indentation
* @return string
*/
public static function DumpSubjects($indstr=' ', $ind='')
{
$r = $ind.join(', ', array_map(
create_function('$v', 'return "{$v[\'login\']}({$v[\'cnt\']})";'),
Subjects::GetSubjectsWCnt()
))."\n";
return $r;
} // fn dumpSubjects
/**
* Delete all subjects and membership records
*
* @return void
*/
public static function DeleteData()
{
global $CC_CONFIG, $CC_DBC;
$CC_DBC->query("DELETE FROM ".$CC_CONFIG['subjTable']);
$CC_DBC->query("DELETE FROM ".$CC_CONFIG['smembTable']);
//ObjClasses::DeleteData();
} // fn deleteData
/**
* Insert test data
*
* @return array
*/
public function TestData()
{
// $tdata = ObjClasses::TestData();
// $o['root'] = Subjects::AddSubj('root', 'q');
// $o['test1'] = Subjects::AddSubj('test1', 'a');
// $o['test2'] = Subjects::AddSubj('test2', 'a');
// $o['test3'] = Subjects::AddSubj('test3', 'a');
// $o['test4'] = Subjects::AddSubj('test4', 'a');
// $o['test5'] = Subjects::AddSubj('test5', 'a');
// $o['gr1'] = Subjects::AddSubj('gr1');
// $o['gr2'] = Subjects::AddSubj('gr2');
// $o['gr3'] = Subjects::AddSubj('gr3');
// $o['gr4'] = Subjects::AddSubj('gr4');
// Subjects::AddSubjectToGroup('test1', 'gr1');
// Subjects::AddSubjectToGroup('test2', 'gr2');
// Subjects::AddSubjectToGroup('test3', 'gr3');
// Subjects::AddSubjectToGroup('test4', 'gr4');
// Subjects::AddSubjectToGroup('test5', 'gr1');
// Subjects::AddSubjectToGroup('gr4', 'gr3');
// Subjects::AddSubjectToGroup('gr3', 'gr2');
// $tdata['subjects'] = $o;
// return $tdata;
} // fn TestData
/**
* Make basic test
*
*/
public static function Test()
{
// $p = ObjClasses::Test();
// if (PEAR::isError($p)) {
// return $p;
// }
// Subjects::DeleteData();
// Subjects::TestData();
// $test_correct = "root(0), test1(0), test2(0), test3(0),".
// " test4(0), test5(0), gr1(2), gr2(2), gr3(2), gr4(1)\n";
// $test_dump = Subjects::DumpSubjects();
// Subjects::RemoveSubj('test1');
// Subjects::RemoveSubj('test3');
// Subjects::RemoveSubjectFromGroup('test5', 'gr1');
// Subjects::RemoveSubjectFromGroup('gr3', 'gr2');
// $test_correct .= "root(0), test2(0), test4(0), test5(0),".
// " gr1(0), gr2(1), gr3(1), gr4(1)\n";
// $test_dump .= Subjects::DumpSubjects();
// Subjects::DeleteData();
// if ($test_dump == $test_correct) {
// $test_log .= "subj: OK\n";
// return TRUE;
// } else {
// return PEAR::raiseError(
// 'Subjects::test:', 1, PEAR_ERROR_DIE, '%s'.
// "<pre>\ncorrect:\n{$test_correct}\n".
// "dump:\n{$test_dump}\n</pre>\n");
// }
} // fn test
} // class Subjects
?>