This commit is contained in:
Richard Underwood 2018-06-07 08:44:46 +00:00 committed by GitHub
commit 20a70838b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 1451 additions and 243 deletions

View file

@ -10,6 +10,12 @@ $logging = TRUE;
$allowclearlogs = TRUE; # Allow clearing of log entries
$allowrotatelogs = FALSE;# Allow rotation to text file on server
$restrictediting = TRUE; # Restrict editing of record types
$restrictedtypes = array(
'SOA' => 1,
'NS' => 1
);
# Log directory - if allowrotatelogs is set, this is where the logs will
# be written. It must be writeable by the web server user.
$logsdirectory = "../etc";

60
includes/database.inc.php Normal file
View file

@ -0,0 +1,60 @@
<?php
// matches version in scheme.sql
$db_version=2;
// Initialise a new DB with latest version
function init_db() {
global $authdb, $db;
is_dir(dirname($authdb)) || mkdir(dirname($authdb));
$db = new SQLite3($authdb, SQLITE3_OPEN_CREATE|SQLITE3_OPEN_READWRITE);
$createsql = file_get_contents('includes/scheme.sql');
$db->exec($createsql);
$salt = bin2hex(openssl_random_pseudo_bytes(16));
$db->exec("INSERT INTO users (emailaddress, password, isadmin) VALUES ('admin', '".crypt("admin", '$6$'.$salt)."', 1)");
return $db;
}
function open_db() {
global $authdb, $db, $db_version;
if (!isset($db)) {
$db = new SQLite3($authdb, SQLITE3_OPEN_READWRITE);
$db->exec('PRAGMA foreign_keys = 1');
}
$version = intval($db->querySingle('SELECT value FROM metadata WHERE name = "version"'));
switch($version) {
case 0:
$sql = file_get_contents('includes/upgrade-0-1.sql');
$db->exec($sql);
writelog("Upgraded schema to version 1","system");
// continue
case 1: // never existed
$sql = file_get_contents('includes/upgrade-1-2.sql');
$db->exec($sql);
writelog("Upgraded schema to version 2","system");
// continue
case $db_version:
break;
}
return $db;
}
function get_db() {
global $authdb, $db;
if (!isset($db)) {
open_db();
}
return $db;
}
?>

176
includes/groups.inc.php Normal file
View file

@ -0,0 +1,176 @@
<?php
function get_all_groups() {
$db = get_db();
$r = $db->query('SELECT id, name, desc FROM groups ORDER BY name');
$ret = array();
while ($row = $r->fetchArray(SQLITE3_ASSOC)) {
array_push($ret, $row);
}
return $ret;
}
function get_group_info($name) {
$db = get_db();
$q = $db->prepare('SELECT * FROM groups WHERE name = ?');
$q->bindValue(1, $name);
$result = $q->execute();
$groupinfo = $result->fetchArray(SQLITE3_ASSOC);
return $groupinfo;
}
function get_group_name($id) {
$db = get_db();
$q = $db->prepare('SELECT name FROM groups WHERE id = ?');
$q->bindValue(1, $id, SQLITE3_INTEGER);
$r = $q->execute();
$ret = $r->fetchArray(SQLITE3_NUM);
if($ret) {
return $ret[0];
} else {
return null;
}
}
function group_exists($name) {
return (bool) get_group_info($name);
}
function add_group($name, $desc) {
$db = get_db();
$q = $db->prepare('INSERT INTO groups (name, desc) VALUES (?, ?)');
$q->bindValue(1, $name, SQLITE3_TEXT);
$q->bindValue(2, $desc, SQLITE3_TEXT);
$ret = $q->execute();
writelog("Added group $name ($desc).");
return $ret;
}
function update_group($id, $name, $desc) {
$db = get_db();
$q = $db->prepare('SELECT * FROM groups WHERE id = ?');
$q->bindValue(1, $id, SQLITE3_INTEGER);
$result = $q->execute();
$groupinfo = $result->fetchArray(SQLITE3_ASSOC);
$q->close();
$oldname = $groupinfo['name'];
$q = $db->prepare('UPDATE groups SET name = ?, desc = ? WHERE id = ?');
$q->bindValue(1, $name, SQLITE3_TEXT);
$q->bindValue(2, $desc, SQLITE3_TEXT);
$q->bindValue(3, $id, SQLITE3_INTEGER);
writelog("Updating group $oldname to: $name ($desc) ");
$ret = $q->execute();
return $ret;
}
function delete_group($id) {
$db = get_db();
$q = $db->prepare('SELECT * FROM groups WHERE id = ?');
$q->bindValue(1, $id, SQLITE3_INTEGER);
$result = $q->execute();
$groupinfo = $result->fetchArray(SQLITE3_ASSOC);
$q->close();
if($groupinfo) {
$q = $db->prepare('DELETE FROM groups WHERE id = ?');
$q->bindValue(1, $id, SQLITE3_INTEGER);
$ret = $q->execute();
writelog("Deleted group " . $groupinfo['name'] . ".");
return $ret;
} else {
return false;
}
}
function valid_group($name) {
return ( bool ) preg_match( "/^[a-z0-9@_.-]+$/i" , $name );
}
function get_group_members($id) {
$db = get_db();
$q = $db->prepare('SELECT groupmembers.id,users.emailaddress AS user FROM groupmembers,users WHERE "group" = ? AND groupmembers.user = users.id');
$q->bindValue(1, $id, SQLITE3_INTEGER);
$result = $q->execute();
$ret = array();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
array_push($ret, $row);
}
return $ret;
}
function get_group_id($group) {
$info=get_group_info($group);
if($info) {
return $info['id'];
} else {
return null;
}
}
function is_group_member($id,$user) {
$db = get_db();
$q = $db->prepare('SELECT id FROM groupmembers WHERE "group" = ? AND user = ?');
$q->bindValue(1, $id, SQLITE3_INTEGER);
$q->bindValue(2, get_user_id($user), SQLITE3_INTEGER);
$r = $q->execute();
$ret = $r->fetchArray(SQLITE3_NUM);
return (bool) $ret;
}
function add_group_member($id,$user) {
$db = get_db();
$userid=get_user_id($user);
$q = $db->prepare('INSERT INTO groupmembers ("group", user) VALUES (?, ?)');
$q->bindValue(1, $id, SQLITE3_INTEGER);
$q->bindValue(2, $userid, SQLITE3_INTEGER);
$ret = $q->execute();
if($ret) {
writelog("Added user $user to group " . get_group_name($id) . ".");
return $db->lastInsertRowID();
} else {
writelog("Failed to add user $user to group " . get_group_name($id) . ".");
return null;
}
}
function remove_group_member($id) {
$db = get_db();
$q = $db->prepare('SELECT groups.name,users.emailaddress FROM groupmembers,users,groups WHERE groupmembers.id=? AND groupmembers.user=users.id AND groupmembers."group"=groups.id');
$q->bindValue(1, $id, SQLITE3_INTEGER);
$r = $q->execute();
$ret = $r->fetchArray(SQLITE3_NUM);
$group=$ret[0];
$user=$ret[1];
$q->close();
$q = $db->prepare('DELETE FROM groupmembers WHERE id=?');
$q->bindValue(1, $id, SQLITE3_INTEGER);
$ret = $q->execute();
if($ret) {
writelog("Removed user $user from group $group.");
} else {
writelog("Failed to remove user $user from group $group.");
}
return $ret;
}
?>

View file

@ -1,6 +1,7 @@
<?php
include('config.inc.php');
include_once('config.inc.php');
include_once('database.inc.php');
$blocklogin = FALSE;
@ -59,13 +60,12 @@ if (function_exists('openssl_random_pseudo_bytes') === FALSE) {
$defaults['defaulttype'] = ucfirst(strtolower($defaults['defaulttype']));
if (isset($authdb) && !file_exists($authdb) && class_exists('SQLite3')) {
is_dir(dirname($authdb)) || mkdir(dirname($authdb));
$db = new SQLite3($authdb, SQLITE3_OPEN_CREATE|SQLITE3_OPEN_READWRITE);
$createsql = file_get_contents('includes/scheme.sql');
$db->exec($createsql);
$salt = bin2hex(openssl_random_pseudo_bytes(16));
$db->exec("INSERT INTO users (emailaddress, password, isadmin) VALUES ('admin', '".crypt("admin", '$6$'.$salt)."', 1)");
if(class_exists('SQLite3')) {
if (isset($authdb) && !file_exists($authdb)) {
init_db();
} else {
open_db();
}
}
function string_starts_with($string, $prefix)
@ -84,17 +84,6 @@ function string_ends_with($string, $suffix)
return (substr($string, -$length) === $suffix);
}
function get_db() {
global $authdb, $db;
if (!isset($db)) {
$db = new SQLite3($authdb, SQLITE3_OPEN_READWRITE);
$db->exec('PRAGMA foreign_keys = 1');
}
return $db;
}
function get_all_users() {
$db = get_db();
$r = $db->query('SELECT id, emailaddress, isadmin FROM users ORDER BY emailaddress');
@ -106,6 +95,40 @@ function get_all_users() {
return $ret;
}
/* Fetches a list of usernames from the DB for autocomplete.
* Restricts list by $term which can appear anywhere in the username
* Restricts results to $num responses
*/
function get_usernames_filtered($term, $num = 10) {
$db = get_db();
$q = $db->prepare("SELECT emailaddress FROM users WHERE emailaddress LIKE ? ORDER BY emailaddress LIMIT 0, ?");
$q->bindValue(1, "%" . $term . "%", SQLITE3_TEXT);
$q->bindValue(2, $num, SQLITE3_INTEGER);
$r = $q->execute();
$ret = array();
while ($row = $r->fetchArray(SQLITE3_NUM)) {
array_push($ret, $row[0]);
}
return $ret;
}
function get_groups_filtered($term, $num = 10) {
$db = get_db();
$q = $db->prepare("SELECT name FROM groups WHERE name LIKE ? ORDER BY name LIMIT 0, ?");
$q->bindValue(1, "%" . $term . "%", SQLITE3_TEXT);
$q->bindValue(2, $num, SQLITE3_INTEGER);
$r = $q->execute();
$ret = array();
while ($row = $r->fetchArray(SQLITE3_NUM)) {
array_push($ret, $row[0]);
}
return $ret;
}
function get_user_info($u) {
$db = get_db();
$q = $db->prepare('SELECT * FROM users WHERE emailaddress = ?');
@ -216,6 +239,10 @@ function valid_user($name) {
}
function jtable_respond($records, $method = 'multiple', $msg = 'Undefined errormessage') {
if($records == null) {
$records=array();
}
$jTableResult = array();
if ($method == 'error') {
$jTableResult['Result'] = "ERROR";
@ -355,13 +382,6 @@ function writelog($line, $user=False) {
try {
$db = get_db();
$q = $db->prepare('CREATE TABLE IF NOT EXISTS logs (
id INTEGER PRIMARY KEY,
user TEXT NOT NULL,
log TEXT NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP);');
$ret = $q->execute();
$q = $db->prepare('INSERT INTO logs (user, log) VALUES (:user, :log)');
$q->bindValue(':user', $user, SQLITE3_TEXT);
$q->bindValue(':log', $line, SQLITE3_TEXT);
@ -455,4 +475,53 @@ if (!function_exists('hash_pbkdf2')) {
}
}
// get user id from name
function get_user_id($user) {
$info=get_user_info($user);
if($info) {
return $info['id'];
} else {
return null;
}
}
// get zone id from name
function get_zone_id($zone) {
$db = get_db();
$q = $db->prepare('SELECT id FROM zones WHERE zone=?');
$q->bindValue(1, $zone, SQLITE3_TEXT);
$r = $q->execute();
$ret = $r->fetchArray(SQLITE3_NUM);
if($ret) {
return $ret[0];
} else {
return null;
}
}
// get user name from id
function get_user_name($userid) {
$db = get_db();
$q = $db->prepare('SELECT emailAddress FROM users WHERE id = ?');
$q->bindValue(1, $userid, SQLITE3_INTEGER);
$r = $q->execute();
$ret = $r->fetchArray(SQLITE3_NUM);
if($ret) {
return $ret[0];
} else {
return null;
}
}
// Include functions for group management
include_once('groups.inc.php');
// Include functions for permissions management
include_once('permissions.inc.php');
?>

View file

@ -0,0 +1,253 @@
<?php
/*
* Permissions.
*
* Set on either users or groups.
* User permissions override any permissions on groups (as are more specific)
* Group permissions are additive
* "Account" renamed as "Owner" in interface, and will always have full permissions.
*
* Bitmask:
* 0x01 - View
* 0x02 - Update non-special records
* 0x04 - Update special records
* 0x08 - Admin (e.g. change permissions)
*
* The interface will use combinations of these shown in the permissionsmap below.
*
*/
$permissionmap=array(
'0' => 'No permissions',
'1' => 'View Only',
'3' => 'Update normal records',
'7' => 'Update all records',
'15' => 'Admin'
);
define('PERM_VIEW',0x01);
define('PERM_UPDATE',0x02);
define('PERM_UPDATESPECIAL',0x04);
define('PERM_ADMIN',0x08);
define('PERM_ALL',0xffff);
// Interface function - Return an array of permissions for the zone
function get_zone_permissions($zone) {
$db = get_db();
$q = $db->prepare('SELECT p.id,p.user,u.emailAddress AS uname,p."group",g.name AS gname, p.permissions FROM permissions p LEFT JOIN users u ON p.user=u.id LEFT JOIN groups g ON p."group"=g.id LEFT JOIN zones z ON p.zone=z.id WHERE z.zone=?');
$q->bindValue(1, $zone, SQLITE3_TEXT);
$result = $q->execute();
$ret = array();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$row2 = array();
$row2['id']=$row['id'];
if($row['user']>0) {
$row2['type']='user';
$row2['value']=$row['uname'];
} else {
$row2['type']='group';
$row2['value']=$row['gname'];
}
$row2['permissions']=$row['permissions'];
array_push($ret, $row2);
}
return $ret;
}
// Interface function - Set permissions for a zone - either userid or groupid should be null
function set_permissions($userid,$groupid,$zone,$permissions) {
global $permissionmap;
$db = get_db();
$q = $db->prepare('INSERT INTO permissions (zone,user,"group",permissions) VALUES (?,?,?,?)');
$q->bindValue(1, get_zone_id($zone), SQLITE3_INTEGER);
$q->bindValue(2, $userid, SQLITE3_INTEGER);
$q->bindValue(3, $groupid, SQLITE3_INTEGER);
$q->bindValue(4, $permissions, SQLITE3_INTEGER);
$ret = $q->execute();
if(!is_null($userid)) {
$who="user " . get_user_name($userid);
} else {
$who="group " . get_group_name($groupid);
}
if($ret) {
writelog("Added '$permissionmap[$permissions]' permissions for $who from zone $zone.");
return $db->lastInsertRowID();
} else {
writelog("Failed to add permissions to zone $zone for $who.");
return null;
}
}
// Interface function - Copy permissions from one zone to another - used for cloning, so assumes no existing permissions on the domain
function copy_permissions($srczone,$dstzone) {
$db = get_db();
$q = $db->prepare('SELECT p.user,p."group",p.permissions FROM permissions p, zones z WHERE p.zone=z.id AND z.zone=?');
$q->bindValue(1, $srczone, SQLITE3_TEXT);
$result = $q->execute();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
set_permissions($row['user'],$row['group'],$dstzone,$row['permissions']);
}
return null;
}
// Interface function - Update permissions for a zone
function update_permissions($id,$permissions) {
global $permissionmap;
$db = get_db();
$q = $db->prepare('SELECT p.permissions, u.emailAddress, g.name, z.zone FROM permissions p LEFT JOIN users u ON p.user=u.id LEFT JOIN groups g ON p."group"=g.id LEFT JOIN zones z ON p.zone=z.id WHERE p.id=?');
$q->bindValue(1, $id, SQLITE3_INTEGER);
$r = $q->execute();
$ret = $r->fetchArray(SQLITE3_NUM);
if($ret[1]!='') {
$who="user " . $ret[1];
} else {
$who="group " . $ret[2];
}
$before=$ret[0];
$zone=$ret[3];
$q->close();
$q = $db->prepare('UPDATE permissions SET permissions=? WHERE id=?');
$q->bindValue(1, $permissions, SQLITE3_INTEGER);
$q->bindValue(2, $id, SQLITE3_INTEGER);
$ret = $q->execute();
if($ret) {
writelog("Permissions changed on zone $zone for $who from '$permissionmap[$before]' to '$permissionmap[$permissions]'.");
return $db->lastInsertRowID();
} else {
writelog("Failed to set permissions on zone $zone for $who (permissions id $id).");
return null;
}
}
// Interface function - Remove permissions from a zone
function remove_permissions($id) {
global $permissionmap;
$db = get_db();
$q = $db->prepare('SELECT p.permissions, u.emailAddress, g.name, z.zone FROM permissions p LEFT JOIN users u ON p.user=u.id LEFT JOIN groups g ON p."group"=g.id LEFT JOIN zones z ON p.zone=z.id WHERE p.id=?');
$q->bindValue(1, $id, SQLITE3_INTEGER);
$r = $q->execute();
$ret = $r->fetchArray(SQLITE3_NUM);
if($ret[1]!='') {
$who="user " . $ret[1];
} else {
$who="group " . $ret[2];
}
$before=$ret[0];
$zone=$ret[3];
$q->close();
$q = $db->prepare('DELETE FROM permissions WHERE id=?');
$q->bindValue(1, $id, SQLITE3_INTEGER);
$ret = $q->execute();
if($ret) {
writelog("Removed '$permissionmap[$before]' permissions for $who from zone $zone");
} else {
writelog("Failed to remove permissions for $who from zone $zone (permissions id $id).");
}
return $ret;
}
// Utility function - Return the permissions set on the zone for this user *not including any group membership*
function user_permissions($zone,$userid) {
$db = get_db();
$q = $db->prepare('SELECT p.permissions FROM permissions p LEFT JOIN zones z ON p.zone=z.id WHERE p.user=? AND z.zone=?');
$q->bindValue(1, $userid, SQLITE3_INTEGER);
$q->bindValue(2, $zone, SQLITE3_TEXT);
$r = $q->execute();
if($r) {
$ret = $r->fetchArray(SQLITE3_NUM);
return $ret[0];
} else {
return null;
}
}
// Utility function - Return the permissions set on the zone for this group
function group_permissions($zone,$groupid) {
$db = get_db();
$q = $db->prepare('SELECT p.permissions FROM permissions p LEFT JOIN zones z ON p.zone=z.id WHERE p."group"=? AND z.zone=?');
$q->bindValue(1, $groupid, SQLITE3_INTEGER);
$q->bindValue(2, $zone, SQLITE3_TEXT);
$r = $q->execute();
if($r) {
$ret = $r->fetchArray(SQLITE3_NUM);
return $ret[0];
} else {
return null;
}
}
// utility function - get the owner of the domain. Move to misc?
function zone_owner($zone) {
$db = get_db();
$q = $db->prepare('SELECT owner FROM zones WHERE zones.zone=?');
$q->bindValue(1,$zone,SQLITE3_TEXT);
$r = $q->execute();
if($r) {
$ret = $r->fetchArray(SQLITE3_NUM);
return $ret[0];
} else {
return null;
}
}
// Utility function - Return the calculated permissions for this user/zone
function permissions($zone,$userid) {
if(is_adminuser() || ($userid == zone_owner($zone))) {
return PERM_ALL;
}
$perm=user_permissions($zone,$userid);
if(!is_null($perm)) {
return $perm;
} else {
$perm=0;
$zoneid=get_zone_id($zone);
$db = get_db();
$q = $db->prepare('SELECT p.permissions FROM groupmembers gm LEFT JOIN permissions p ON p."group"=gm."group" WHERE zone=? AND p."group">0 AND gm.user=?');
$q->bindValue(1, $zoneid, SQLITE3_INTEGER);
$q->bindValue(2, $userid, SQLITE3_INTEGER);
$r = $q->execute();
while ($row = $r->fetchArray(SQLITE3_NUM)) {
$perm=$perm|$row[0];
}
return $perm;
}
}
// Utility function - check a permission for current user
function check_permissions($zone,$permmask) {
return (bool) (permissions($zone,get_user_id(get_sess_user()))&$permmask);
}
?>

View file

@ -12,3 +12,39 @@ CREATE TABLE zones (
owner INTEGER NOT NULL,
UNIQUE(zone),
FOREIGN KEY(owner) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE );
CREATE TABLE logs (
id INTEGER PRIMARY KEY,
user TEXT NOT NULL,
log TEXT NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP);
CREATE TABLE groups (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR UNIQUE NOT NULL,
desc VARCHAR);
CREATE TABLE groupmembers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
"group" INTEGER NOT NULL,
user INTEGER NOT NULL,
UNIQUE("group",user),
FOREIGN KEY("group") REFERENCES groups(id) ON DELETE CASCADE,
FOREIGN KEY(user) REFERENCES users(id) ON DELETE CASCADE);
CREATE TABLE permissions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
zone INTEGER NOT NULL,
user INTEGER,
"group" INTEGER,
permissions INTEGER,
UNIQUE(zone,user,"group"),
FOREIGN KEY(zone) REFERENCES zones(id) ON DELETE CASCADE,
FOREIGN KEY(user) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY("group") REFERENCES groups(id) ON DELETE CASCADE);
CREATE TABLE metadata (
name VARCHAR PRIMARY KEY,
value VARCHAR NOT NULL);
INSERT INTO metadata (name, value) VALUES ("version","2");

5
includes/upgrade-0-1.sql Normal file
View file

@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS logs (
id INTEGER PRIMARY KEY,
user TEXT NOT NULL,
log TEXT NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP);

29
includes/upgrade-1-2.sql Normal file
View file

@ -0,0 +1,29 @@
CREATE TABLE groups (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR UNIQUE NOT NULL,
desc VARCHAR);
CREATE TABLE groupmembers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
"group" INTEGER NOT NULL,
user INTEGER NOT NULL,
UNIQUE("group",user),
FOREIGN KEY("group") REFERENCES groups(id) ON DELETE CASCADE,
FOREIGN KEY(user) REFERENCES users(id) ON DELETE CASCADE);
CREATE TABLE permissions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
zone INTEGER NOT NULL,
user INTEGER,
"group" INTEGER,
permissions INTEGER,
UNIQUE(zone,user,"group"),
FOREIGN KEY(zone) REFERENCES zones(id) ON DELETE CASCADE,
FOREIGN KEY(user) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY("group") REFERENCES groups(id) ON DELETE CASCADE);
CREATE TABLE metadata (
name VARCHAR PRIMARY KEY,
value VARCHAR NOT NULL);
INSERT INTO metadata (name, value) VALUES ("version","2");