davadmin/admin/users.php

390 lines
11 KiB
PHP

<?php
/**
* User administration code.
*
* @author Jan Dittberner <jan@dittberner.info>
* @version $Id$
* @license GPL
* @package DAVAdmin
*
* Copyright (c) 2007, 2008 Jan Dittberner
*
* This file is part of DAVAdmin.
*
* DAVAdmin 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.
*
* DAVAdmin 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 DAVAdmin; if not, see <http://www.gnu.org/licenses/>.
*/
/** Include common code. */
include_once('common.inc.php');
/**
* Gets the names of the given users's groups from the group file.
*
* @param string $username user name
* @return array array of group names
* @access private
*/
function _getGroupNames($username) {
$groupdata = file($GLOBALS['davconfig']['group.file']);
$retval = array();
foreach ($groupdata as $line) {
list($group, $users) = explode(":", $line);
$group = trim($group);
$users = explode(" ", $users);
foreach ($users as $user) {
if (trim($user) == $username) {
array_push($retval, $group);
}
}
}
asort($retval);
return $retval;
}
/**
* Gets XML encoded data for a user.
*
* @param int $uid user id
* @return string XML string
*/
function getUserData($uid) {
if (!(is_numeric($uid) && array_key_exists($uid, $GLOBALS['namemap']))) {
errorAsXml(sprintf(_("Invalid user id %s"), $uid));
}
$row = $GLOBALS['namemap'][$uid];
$groups = _getGroupNames($row['username']);
$retval = sprintf('<?xml version="1.0" encoding="utf8"?><userdata><uid>%d</uid><username>%s</username><firstname>%s</firstname><lastname>%s</lastname><groups>%s</groups><loggedin>0</loggedin></userdata>',
$uid, $row['username'], $row['firstname'],
$row['lastname'], implode(", ", $groups));
header("Content-Type: text/xml; charset=UTF-8");
return $retval;
}
/**
* Gets XML encoded data for a deleted user.
*
* @param int $uid user id
* @return string XML string
*/
function getDeletedUserData($uid) {
if (!is_numeric($uid)) {
errorAsXml(sprintf(_("Invalid user id %s"), $uid));
}
return sprintf('<?xml version="1.0" encoding="utf8"?><userdata><uid>%d</uid></userdata>', $uid);
}
/**
* Validates the given user data array for correctness.
*
* @param array &$userdata reference to an user data array
* @param boolean $forinsert if this is true the check skips field
* that will be created during insert
* @return array an array with validation error messages or an empty
* array
*/
function validateUserData(&$userdata, $forinsert) {
$errormsgs = array();
// normalize the user data array
foreach ($userdata as $key => $value) {
$userdata[$key] = trim($value);
}
if (!$forinsert) {
if (!is_numeric($userdata['uid'])) {
array_push($errormsgs, _("Uid must be numeric."));
}
if (!empty($userdata['password']) && strlen($userdata['password']) < 8) {
array_push($errormsgs, _("Password must be at least 8 characters long."));
}
} else {
if (!preg_match('/^[a-zA-Z0-9]{2,}$/', $userdata['username'])) {
array_push($errormsgs, _("Username must be at least 2 characters long and must contain letters and digits only."));
}
if (empty($userdata['password']) || strlen($userdata['password']) < 8) {
array_push($errormsgs, _("Password must be at least 8 characters long."));
}
}
if (empty($userdata['firstname'])) {
$userdata['firstname'] = null;
}
if (empty($userdata['lastname'])) {
$userdata['lastname'] = null;
}
if (!preg_match('/^[0-9a-zA-z]+(\s*,\s*[0-9a-zA-z]+)*$/', $userdata['groups'])) {
array_push($errormsgs, _('Groups must be a list of group names separated by commas. Group names must consist of letters and digits.'));
}
return $errormsgs;
}
/**
* Create an entry for a digest authentication file.
*
* @param string $username user name
* @param string $realm realm name
* @param string $password password
* @return string digest entry
*/
function createDigest($username, $realm, $password) {
return sprintf("%s:%s:%s", $username, $realm,
md5(sprintf("%s:%s:%s", $username, $realm, $password)));
}
/**
* Update the user to name mapping file.
*/
function updateNameMap() {
$fh = fopen($GLOBALS['davconfig']['namemap.file'], 'w');
fwrite($fh, json_encode($GLOBALS['namemap']));
fclose($fh);
}
/**
* Update the digest file with the user information.
*
* @param &array reference to an associative array of user data
*/
function updateDigest(&$userdata) {
if ($userdata['password']) {
$digests = file($GLOBALS['davconfig']['digest.file']);
$written = false;
$fh = fopen($GLOBALS['davconfig']['digest.file'], 'w');
foreach ($digests as $digest) {
list($username, $realm, $data) = explode(":", $digest);
if ($username == $userdata['username']
&& $realm == $GLOBALS['davconfig']['dav.realm']) {
fwrite($fh, createDigest($userdata['username'],
$GLOBALS['davconfig']['dav.realm'],
$userdata['password']) . "\n");
$written = true;
} else {
fwrite($fh, $digest);
}
}
if (!$written) {
fwrite($fh, createDigest($userdata['username'],
$GLOBALS['davconfig']['dav.realm'],
$userdata['password']) . "\n");
}
fclose($fh);
}
}
/**
* Update the group file with the group assignments of the user.
*
* @param &array reference to an associative array of user data
*/
function updateGroups(&$userdata) {
if ($userdata['groups']) {
$written = array();
foreach (explode(",", $userdata['groups']) as $group) {
$written[trim($group)] = false;
}
$groupdata = file($GLOBALS['davconfig']['group.file']);
$fh = fopen($GLOBALS['davconfig']['group.file'], 'w');
foreach ($groupdata as $groupline) {
list ($group, $users) = explode(":", $groupline);
$group = trim($group);
$users = explode(" ", trim($users));
foreach ($users as $key => $user) {
$users[$key] = trim($user);
}
if (array_key_exists($group, $written)) {
if (!in_array($userdata['username'], $users)) {
array_push($users, $userdata['username']);
}
fprintf($fh, "%s: %s\n", $group, implode(" ", $users));
$written[$group] = true;
} else {
if (in_array($userdata['username'], $users)) {
array_splice($users, array_search($userdata['username'], $users),1);
}
if (count($users) > 0 ) {
fprintf($fh, "%s: %s\n", $group, implode(" ", $users));
}
}
}
foreach ($written as $group => $done) {
if (!$done) {
fprintf($fh, "%s: %s\n", $group, $userdata['username']);
}
}
fclose($fh);
}
}
/**
* Updates the data of a user in the database.
*
* @param array &$userdata reference to a user data array
*/
function updateUser(&$userdata) {
$validation = validateUserData($userdata, false);
if (!empty($validation)) {
errorAsXml(implode("\n", $validation));
}
$GLOBALS['namemap'][$userdata['uid']] =
array('uid' => $userdata['uid'],
'username' => $userdata['username'],
'firstname' => $userdata['firstname'],
'lastname' => $userdata['lastname']);
// write name mapping data
updateNameMap();
// write digest data
updateDigest($userdata);
// update group file
updateGroups($userdata);
}
/**
* Inserts a new user and its group assignments into the database.
*
* @param array &$userdata reference to an user data array
*/
function insertUser(&$userdata) {
$validation = validateUserData($userdata, true);
if (!empty($validation)) {
errorAsXml(implode("\n", $validation));
}
$uids = array_keys($GLOBALS['namemap']);
if (empty($uids)) {
$uid = 1;
} else {
foreach ($uids as $currentuid) {
$uid = max($uid, $currentuid);
}
$uid += 1;
}
$GLOBALS['namemap'][$uid] =
array('uid' => $uid,
'username' => $userdata['username'],
'firstname' => $userdata['firstname'],
'lastname' => $userdata['lastname']);
// write name mapping data
updateNameMap();
// write digest data
updateDigest($userdata);
// update group file
updateGroups($userdata);
return $uid;
}
/**
* Remove the digest entries of the given user from the digest file.
*
* @param string $username
*/
function removeDigest($username) {
$digests = file($GLOBALS['davconfig']['digest.file']);
$fh = fopen($GLOBALS['davconfig']['digest.file'], 'w');
foreach ($digests as $digest) {
list($user, $realm, $data) = explode(":", $digest);
if (!($user == $username
&& $realm == $GLOBALS['davconfig']['dav.realm'])) {
fwrite($fh, $digest);
}
}
fclose($fh);
}
/**
* Remove the given user from all groups in the group file.
*
* @param string $username
*/
function removeFromGroups($username) {
$groupdata = file($GLOBALS['davconfig']['group.file']);
$fh = fopen($GLOBALS['davconfig']['group.file'], 'w');
foreach ($groupdata as $groupline) {
list ($group, $users) = explode(":", $groupline);
$group = trim($group);
$users = explode(" ", trim($users));
foreach ($users as $key => $user) {
$users[$key] = trim($user);
}
if (in_array($username, $users)) {
array_splice($users, array_search($username, $users),1);
}
if (count($users) > 0 ) {
fprintf($fh, "%s: %s\n", $group, implode(" ", $users));
}
}
fclose($fh);
}
/**
* Delete the user with the given user id and its group assignments
* from the database.
*
* @param int $uid user id
*/
function deleteUser($uid) {
if (!(is_numeric($uid) && array_key_exists($uid, $GLOBALS['namemap']))) {
errorAsXml(sprintf(_("Invalid user id %s"), $uid));
}
removeDigest($GLOBALS['namemap'][$uid]['username']);
removeFromGroups($GLOBALS['namemap'][$uid]['username']);
unset($GLOBALS['namemap'][$uid]);
updateNameMap();
}
if ($_GET) {
if ($_GET['method']) {
switch ($_GET['method']) {
case 'getuserdata':
if ($_GET['uid']) {
print getUserData($_GET['uid']);
}
break;
default:
errorAsXml(sprintf(_("Unexpected values %s!"), serialize($_GET)));
}
} else if (isset($_GET['language'])) {
header("Content-Type: text/html; charset=UTF-8");
$smarty->assign("users", $namemap);
$smarty->display("users.html");
} else {
invalidCall();
}
} elseif ($_POST) {
if ($_POST['method']) {
switch ($_POST['method']) {
case 'submituser':
if ($_POST['uid']) {
updateUser($_POST);
print getUserData($_POST['uid']);
} else {
print getUserData(insertUser($_POST));
}
break;
case 'deleteuser':
if ($_POST['uid']) {
deleteUser($_POST['uid']);
print getDeletedUserData($_POST['uid']);
} else {
errorAsXml(sprintf(_("Unexpected values %s!"), serialize($_POST)));
}
break;
default:
errorAsXml(sprintf(_("Unexpected values %s!"), serialize($_POST)));
}
} else {
invalidCall();
}
} else {
header("Content-Type: text/html; charset=UTF-8");
$smarty->assign("users", $namemap);
$smarty->display("users.html");
}
?>