*
* AuthUser account self-registration and management using database (mysqli)
*
* For more information, please see the online documentation at
* http://www.pmwiki.org/wiki/Cookbook/UserAdmin
*
* This script is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
$RecipeInfo['UserAdmin']['Version'] = '2015-05-11';
#echo "DEBUG: useradmin-dbase: Being included
\n";
SDV($AuthUserFunctions['useradmindb'], 'AuthUserDbase');
function AuthUserDbase($pagename, $id, $pw, $pwlist, &$authlist)
{
global $UserAdmin, $AuthUser;
static $first = true;
echo "AUD(pagename=$pagename, id=$id, pw=$pw, pwlist=$pwlist): Entering.
\n";
#echo "DEBUG: authlist=
".print_r($authlist,true)."
\n";
#echo "DEBUG: UserAdmin=".print_r($UserAdmin,true)."
\n";
$row = $UserAdmin->ReadUser($id);
#echo "DEBUG: row=".print_r($row,true)."
\n";
#echo "DEBUG: crypt($pw, $row[userpwhash])="._crypt($pw, $row['userpwhash'])."
\n";
if (!empty($row['userpwhash']) && (_crypt($pw, $row['userpwhash']) == $row['userpwhash'])) {
$groups = $UserAdmin->ReadGroups($id, true);
echo "DEBUG: AUD: groups=".print_r($groups,true)."
\n";
foreach ($groups as $g)
$authlist[$g] = 1;
echo "DEBUG: AUD: authlist=".print_r($authlist,true)."
\n";
#exit; //DEBUG:
return true;
}
#echo "DEBUG: AUD: Returning FALSE
\n";
return false;
}
# If this is uncommented then we can't log in
#if (strncmp($action, 'user', 4)) return;
require_once('useradmin-core.php');
class UserAdminDbase extends UserAdmin {
var $EnableNestedGroups = true; // allow nested groups
var $pdo; // database connection
var $dbprefix = '';
function __construct($dsn, $user, $pass, $opts=null, $prefix='')
{
try {
$this->pdo = new PDO($dsn, $user, $pass, $opts);
} catch (PDOException $e) {
$this->dblog( "Connection failed: ".$e->getMessage(), true);
}
if ($prefix)
$this->dbprefix = $prefix;
}
function dblog($msg, $fatal=true)
{
echo $msg;
if ($fatal) exit;
}
/* check that the db structure has been created */
/*
$sql = "SHOW TABLES LIKE '{$this->dbconfig['prefix']}uadb'";
$result = $conn->query($sql);
if ($result && $result->num_rows > 0) {
$this->conn = $conn;
return $conn;
}
echo "ERROR: Database is not yet set up for UserAdmin Dbase.
\n";
exit(1);
*/
function dbtablename($tbl)
{
if ($this->dbprefix)
$tbl = $this->dbprefix.$tbl;
return $tbl;
}
function dbprepare($sql)
{
if (!$result = $this->pdo->prepare($sql)) {
$errmsg = "DB ERROR: Cannot prepare. SQL=<$sql> error({$this->conn->errno})=$this->conn->error\n";
$this->dberror($errmsg);
}
return $result;
}
function dbquery($sql)
{
if (!$this->dbconnect())
exit; // this shouldn't ever really happen
if (!($stmt = $this->conn->query($sql)) || $this->conn->errno) {
$errmsg = "DB ERROR: Cannot query. SQL=<$sql> error({$this->conn->errno})=$this->conn->error\n";
$this->dberror($errmsg);
}
return $stmt;
}
function dbfetch($result)
{
$rtn = mysqli_fetch_assoc($result);
if ($this->conn->errno) {
$errmsg = "DB ERROR: Cannot fetch. error({$this->conn->errno})=$this->conn->error\n";
$this->dberror($errmsg);
}
return $rtn;
}
function dberror($msg)
{
# This would be a good place for logging
die($msg);
}
function ReadUser($username, $readgroup=false)
{
$sql = "SELECT *
FROM ".$this->dbtablename('users')."
WHERE username = :username ";
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute(array(':username'=>$username));
} catch (PDOException $e) {
$this->dblog("ReadUser(): Error: SQL=$sql ERROR=".$e->getMessage());
}
$row = $stmt->fetch(PDO::FETCH_ASSOC);
# If I do this "union" of results then WriteUser() doesn't know where the read came from
#if (!$row) $row = parent::ReadUser($username, $readgroup);
return $row;
}
function WriteUser($username, $data, $csum='', $auth='read')
{
$row = $this->ReadUser($username);
if ($row) {
# UPDATE
$sql = "UPDATE ".$this->dbtablename('users')." SET ";
$sep = '';
$bindvars = array();
foreach ($data as $k=>$v) {
if ($k == 'username') continue;
$sql .= "$sep$k=:$k";
$bindvars[":$k"] = $v;
$sep = ',';
}
$sql .= " WHERE username = :username ";
$bindvars[':username'] = $username; // needed for the WHERE clause
} else {
# INSERT
$sql = "INSERT INTO ".$this->dbtablename('users')." (username";
$vals= "VALUES (:username";
$bindvars=array(':username'=>$username);
foreach ($data as $k=>$v) {
if ($k == 'username') continue; // already put in above
$sql .= ", $k";
$vals.= ", :$k";
$bindvars[":$k"] = $v;
}
$sql .= ") ".$vals.")";
}
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute($bindvars);
} catch (PDOException $e) {
$this->dblog("WriteUser(): Error SQL=$sql ERROR=".$e->getMessage());
}
return true;
}
function ListGroups($pat=NULL)
{
$sql = "SELECT groupname FROM ".$this->dbtablename('groups');
if ($pat)
$sql .= " WHERE groupname = :groupname";
try {
$stmt = $this->pdo->prepare($sql);
if ($pat) $stmt->execute(array(':groupname'=>$pat)); // do we really need a "pattern" or is groupname enuf?
else $stmt->execute();
} catch (PDOException $e) {
$this->dblog("ListGroups(): Error SQL=$sql ERROR=".$e->getMessage());
}
$rtn = array();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC))
$rtn[] = $row['groupname'];
#echo "DEBUG: ListGroups(): rtn=".print_r($rtn,true)."
\n";
return $rtn;
}
function ListUsers($pat=NULL)
{
$sql = "SELECT *
FROM ".$this->dbtablename('users');
if ($pat)
$sql .= " WHERE username = :username";
try {
$stmt = $this->pdo->prepare($sql);
if ($pat) $stmt->execute(array(':username'=>$pat)); // do we really need a "pattern" or is username enuf?
else $stmt->execute();
} catch (PDOException $e) {
$this->dblog("ListUsers(): Error SQL=$sql ERROR=".$e->getMessage());
}
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
#echo "DEBUG: ListUsers($pat): rows=".print_r($rows,true)."
\n";
$rtn = array();
foreach ($rows as $row)
$rtn[] = $row['username'];
return $rtn;
}
############################################################################
## ReadGroups()
## with username: returns array('@groupA', '@groupB', ...)
## w/o username: returns array('@groupA' => array('userA', 'userB', ...), '@groupB' => ...)
############################################################################
function ReadGroups($username=NULL, $recursive=false)
{
if ($username) {
$sql = "SELECT groupname
FROM ".$this->dbtablename('group_members')."
WHERE username = :username";
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute(array(':username'=>$username));
} catch (PDOException $e) {
$this->dblog("ReadGroups(): Error SQL=$sql ERROR=".$e->getMessage());
}
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$grouplist = array();
foreach($rows as $row) {
if (!in_array($row['groupname'], $grouplist)) {
$grouplist[] = $row['groupname'];
if ($recursive)
$this->NestedGroupParents($row['groupname'], $grouplist);
}
}
return $grouplist;
} else {
$sql = "SELECT groupname
FROM ".$this->dbtablename('groups');
$rtn = array();
foreach ($this->pdo->query($sql) as $row) {
$members = array();
$rtn[$row['groupname']] = $this->NestedGroupUsers($row['groupname'], $members, $recursive);
}
#echo "DEBUG: ReadGroups(): Returning ".print_r($rtn,true)."
\n";
return $rtn;
}
}
function NestedGroupUsers($groupname, &$grouplist, $recursive=false)
{
#echo "DEBUG: NestedGroupUsers($groupname): Entering
\n";
$sql = "SELECT username, user_is_group
FROM ".$this->dbtablename('group_members')."
WHERE groupname = :groupname";
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute(array(':groupname'=>$groupname));
} catch (PDOException $e) {
$this->dblog("NestedGroupUsers(): Error SQL=$sql ERROR=".$e->getMessage());
}
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
#echo "DEBUG: NGU($groupname): rows=".print_r($rows,true)."
\n";
$rtn = array();
foreach($rows as $row) {
if ($recursive) {
if ($row['user_is_group']) {
if (!in_array($row['username'], $grouplist)) {
$grouplist[] = $row['username'];
$rtn = array_merge($rtn, $this->NestedGroupUsers($row['username'], $grouplist, $recursive));
}
} else {
if (!in_array($row['username'], $rtn))
$rtn[] = $row['username'];
}
} else {
if (!in_array($row['username'], $rtn))
$rtn[] = $row['username'];
}
}
#echo "DEBUG: recursive=".print_r($recursive,true)."
\n";
#echo "DEBUG: NGU($groupname): Returning ".print_r($rtn,true)."
\n";
return $rtn;
}
function NestedGroupParents($groupname, &$grouplist)
{
$sql = "SELECT groupname
FROM ".$this->dbtablename('group_members')."
WHERE username = :groupname
AND user_is_group = 1";
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute(array(':groupname'=>$groupname));
} catch (PDOException $e) {
$this->dblog("NestedGroupParents(): Error SQL=$sql ERROR=".$e->getMessage());
}
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach($rows as $row) {
if (!in_array($row['groupname'], $grouplist)) {
$grouplist[] = $row['groupname'];
$this->NestedGroupParents($row['groupname'], $grouplist);
}
}
}
## $add is an array of members to add to group
## $del is an array of members to remove from group
function WriteGroup($groupname, $add, $del, $csum='', $auth='read')
{
$rtn = true;
#echo "DEBUG: add=".print_r($add,true)."
\n";
#echo "DEBUG: del=".print_r($del,true)."
\n";
#
# First check to make sure the group actually exists and, if not, insert it
#
$sql = "SELECT * FROM ".$this->dbtablename('groups')."
WHERE groupname = :groupname";
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute(array(':groupname'=>$groupname));
$groupexists = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
} catch (PDOException $e) {
$this->dblog("WriteGroup(chkexist): Error SQL=$sql ERROR=".$e->getMessage());
$rtn = false;
}
if ($rtn && !$groupexists) {
$sql = "INSERT INTO ".$this->dbtablename('groups')."
(groupname) VALUES (:groupname)";
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute(array(':groupname'=>$groupname));
} catch (PDOException $e) {
$this->dblog("WriteGroup(addgroup): Error SQL=$sql ERROR=".$e->getMessage());
$rtn = false;
}
}
#
# Now delete from and/or insert into group_members as appropriate
#
if ($del) {
$sql = "DELETE FROM ".$this->dbtablename('group_members')."
WHERE groupname = :groupname
AND username = :username";
try {
$stmt = $this->pdo->prepare($sql);
foreach ($del as $d)
$stmt->execute(array(':groupname'=>$groupname, ':username'=>$d));
} catch (PDOException $e) {
$this->dblog("WriteGroup(del): Error SQL=$sql ERROR=".$e->getMessage());
$rtn = false;
}
}
if ($add) {
$sql = "INSERT INTO ".$this->dbtablename('group_members')."
( groupname, username, user_is_group )
VALUES (:groupname, :username, IF(SUBSTR(:username2,1,1)='@', 1, 0))";
try {
$stmt = $this->pdo->prepare($sql);
foreach ($add as $a)
$stmt->execute(array(':groupname'=>$groupname, ':username'=>$a, ':username2'=>$a));
} catch (PDOException $e) {
$this->dblog("WriteGroup(add): Error SQL=$sql ERROR=".$e->getMessage());
$rtn = false;
}
}
return $rtn;
}
function HandleInstall($pagename)
{
if (!$this->Superuser($pagename)) {
echo "You do not have the necessary privileges. Aborting.
\n";
exit(1);
}
/*************************
* DATABASE STRUCTURE:
*************************/
$prefix = $this->dbprefix;
$db_create_queries = array(
"CREATE TABLE `${prefix}users` (
`username` varchar(50) NOT NULL,
`userpwhash` varchar(100) NOT NULL,
`useremail` varchar(250) NOT NULL,
`userrealname` varchar(250) DEFAULT NULL,
`userfname` varchar(150) DEFAULT NULL,
`userlname` varchar(150) DEFAULT NULL,
`userkey` varchar(100) DEFAULT NULL,
`userkeyreason` varchar(100) DEFAULT NULL,
`userkeytime` int(15) DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8",
"CREATE TABLE `${prefix}groups` (
`groupname` varchar(50) NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`groupname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8",
"CREATE TABLE `${prefix}group_members` (
`groupname` varchar(50) NOT NULL,
`username` varchar(50) NOT NULL,
`admin` tinyint(1) NOT NULL DEFAULT '0',
`user_is_group` tinyint(1) NOT NULL DEFAULT '0',
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`groupname`,`username`),
KEY `groupname` (`groupname`),
KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8",
"CREATE TABLE `${prefix}uadb` (
`do_I_exist` varchar(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8",
);
try {
foreach ($db_create_queries as $sql)
$this->pdo->exec($sql);
} catch (PDOException $e) {
$this->dblog("HandleInstall(): ERROR: SQL=$sql ".$e->getMessage());
}
echo "Success. Database Tables Created
\n";
}
}
# This should be called from config.php (or farmconfig.php) AFTER including this file
#$UserAdmin = new UserAdminDbase('dbn', 'user', 'pass', array(options...), 'table_name_prefix');