mirror of
https://github.com/tuxis-ie/nsedit.git
synced 2025-04-19 20:09:14 +03:00
Merge pull request #6 from stbuehler/pull
Fix security bugs (missing access checks, csrf token), fix other bugs (edit records, ..), add some features (masters for slaves, ...)
This commit is contained in:
commit
6aab07d79e
8 changed files with 1137 additions and 489 deletions
28
css/base.css
28
css/base.css
|
@ -139,3 +139,31 @@ pre {
|
|||
.ui-dialog {
|
||||
max-width: 80%;
|
||||
}
|
||||
tr.jtable-data-row td {
|
||||
font-family: monospace;
|
||||
font-size: 16px;
|
||||
vertical-align: top;
|
||||
white-space: pre-wrap; /* css-3 */
|
||||
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
||||
white-space: -pre-wrap; /* Opera 4-6 */
|
||||
white-space: -o-pre-wrap; /* Opera 7 */
|
||||
word-wrap: break-word;
|
||||
}
|
||||
tr.jtable-data-row td.ttl, tr.jtable-data-row td.priority,
|
||||
.jtable-input input.ttl, .jtable-input input.priority {
|
||||
text-align: right;
|
||||
}
|
||||
tr.jtable-data-row td.content {
|
||||
max-width: 800px;
|
||||
}
|
||||
.jtable-input input {
|
||||
font-family: monospace;
|
||||
font-size: 16px;
|
||||
}
|
||||
.jtable-input input.name {
|
||||
width: 100%;
|
||||
}
|
||||
.jtable-input input.content {
|
||||
min-width: 600px;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ $apiuser = ''; # The PowerDNS API username
|
|||
$apipass = ''; # The PowerDNS API-user password
|
||||
$apiip = ''; # The IP of the PowerDNS API
|
||||
$apiport = '8081'; # The port of the PowerDNS API
|
||||
$apisid = ''; # PowerDNS's :server_id
|
||||
$apisid = 'localhost'; # PowerDNS's :server_id
|
||||
$allowzoneadd = FALSE; # Allow normal users to add zones
|
||||
|
||||
|
||||
|
@ -20,6 +20,9 @@ $allowzoneadd = FALSE; # Allow normal users to add zones
|
|||
|
||||
$authdb = "../etc/pdns.users.sqlite3";
|
||||
|
||||
# Set a random generated secret to enable auto-login and long living csrf tokens
|
||||
// $secret = '...';
|
||||
|
||||
$templates = array();
|
||||
/*
|
||||
$templates[] = array(
|
||||
|
@ -27,7 +30,7 @@ $templates[] = array(
|
|||
'owner' => 'username', # Set to 'public' to make it available to all users
|
||||
'records' => array(
|
||||
array(
|
||||
'label' => '',
|
||||
'name' => '',
|
||||
'type' => 'MX',
|
||||
'content' => 'mx2.tuxis.nl',
|
||||
'priority' => '200')
|
||||
|
@ -35,6 +38,7 @@ $templates[] = array(
|
|||
);
|
||||
*/
|
||||
|
||||
$defaults['soa_edit'] = 'INCEPTION-INCREMENT';
|
||||
$defaults['soa_edit_api'] = 'INCEPTION-INCREMENT';
|
||||
$defaults['defaulttype'] = 'Master'; # Choose between 'Native' or 'Master'
|
||||
$defaults['primaryns'] = 'unconfigured.primaryns'; # The value of the first NS-record
|
||||
|
@ -56,5 +60,3 @@ if (!file_exists($authdb)) {
|
|||
$salt = bin2hex(openssl_random_pseudo_bytes(16));
|
||||
$db->exec("INSERT INTO users (emailaddress, password, isadmin) VALUES ('admin', '".crypt("admin", '$6$'.$salt)."', 1)");
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -2,6 +2,22 @@
|
|||
|
||||
include('config.inc.php');
|
||||
|
||||
function string_starts_with($string, $prefix)
|
||||
{
|
||||
$length = strlen($prefix);
|
||||
return (substr($string, 0, $length) === $prefix);
|
||||
}
|
||||
|
||||
function string_ends_with($string, $suffix)
|
||||
{
|
||||
$length = strlen($suffix);
|
||||
if ($length == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (substr($string, -$length) === $suffix);
|
||||
}
|
||||
|
||||
function get_db() {
|
||||
global $authdb;
|
||||
|
||||
|
@ -11,17 +27,11 @@ function get_db() {
|
|||
return $db;
|
||||
}
|
||||
|
||||
function gen_pw() {
|
||||
$password = exec('/usr/bin/pwgen -s -B -c -n 10 -1');
|
||||
return $password;
|
||||
}
|
||||
|
||||
function get_all_users() {
|
||||
$db = get_db();
|
||||
$r = $db->query('SELECT id, emailaddress, isadmin FROM users');
|
||||
$ret = array();
|
||||
while ($row = $r->fetchArray()) {
|
||||
$row['emailaddress'] = htmlspecialchars($row['emailaddress']);
|
||||
while ($row = $r->fetchArray(SQLITE3_ASSOC)) {
|
||||
array_push($ret, $row);
|
||||
}
|
||||
|
||||
|
@ -39,6 +49,10 @@ function get_user_info($u) {
|
|||
return $userinfo;
|
||||
}
|
||||
|
||||
function user_exists($u) {
|
||||
return (bool) get_user_info($u);
|
||||
}
|
||||
|
||||
function do_db_auth($u, $p) {
|
||||
$db = get_db();
|
||||
$q = $db->prepare('SELECT * FROM users WHERE emailaddress = ?');
|
||||
|
@ -46,40 +60,52 @@ function do_db_auth($u, $p) {
|
|||
$result = $q->execute();
|
||||
$userinfo = $result->fetchArray(SQLITE3_ASSOC);
|
||||
$db->close();
|
||||
if (isset($userinfo['password']) and (crypt($p, $userinfo['password']) == $userinfo['password'])) {
|
||||
|
||||
if ($userinfo and $userinfo['password'] and (crypt($p, $userinfo['password']) === $userinfo['password'])) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
function get_pw($username) {
|
||||
$db = get_db();
|
||||
$q = $db->prepare('SELECT password FROM users WHERE emailaddress = ? LIMIT 1');
|
||||
$q->bindValue(1, $username, SQLITE_TEXT);
|
||||
$result = $q->execute();
|
||||
$pw = $result->fetchArray(SQLITE3_ASSOC);
|
||||
$db->close();
|
||||
if (isset($pw['password'])) {
|
||||
return $pw['password'];
|
||||
function add_user($username, $isadmin = FALSE, $password = '') {
|
||||
if (!$password) {
|
||||
$password = bin2hex(openssl_random_pseudo_bytes(32));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
function add_user($username, $isadmin = '0', $password = FALSE) {
|
||||
if ($password === FALSE or $password == "") {
|
||||
$password = get_pw($username);
|
||||
} elseif (!preg_match('/\$6\$/', $password)) {
|
||||
if (!string_starts_with($password, '$6$')) {
|
||||
$salt = bin2hex(openssl_random_pseudo_bytes(16));
|
||||
$password = crypt($password, '$6$'.$salt);
|
||||
}
|
||||
|
||||
$db = get_db();
|
||||
$q = $db->prepare('INSERT OR REPLACE INTO users (emailaddress, password, isadmin) VALUES (?, ?, ?)');
|
||||
$q = $db->prepare('INSERT INTO users (emailaddress, password, isadmin) VALUES (?, ?, ?)');
|
||||
$q->bindValue(1, $username, SQLITE3_TEXT);
|
||||
$q->bindValue(2, $password, SQLITE3_TEXT);
|
||||
$q->bindValue(3, $isadmin, SQLITE3_INTEGER);
|
||||
$q->bindValue(3, (int)(bool) $isadmin, SQLITE3_INTEGER);
|
||||
$ret = $q->execute();
|
||||
$db->close();
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function update_user($username, $isadmin, $password) {
|
||||
if ($password && !preg_match('/\$6\$/', $password)) {
|
||||
$salt = bin2hex(openssl_random_pseudo_bytes(16));
|
||||
$password = crypt($password, '$6$'.$salt);
|
||||
}
|
||||
|
||||
$db = get_db();
|
||||
|
||||
if ($password) {
|
||||
$q = $db->prepare('UPDATE users SET isadmin = ?, password = ? WHERE emailaddress = ?');
|
||||
$q->bindValue(1, (int)(bool)$isadmin, SQLITE3_INTEGER);
|
||||
$q->bindValue(2, $password, SQLITE3_TEXT);
|
||||
$q->bindValue(3, $username, SQLITE3_TEXT);
|
||||
} else {
|
||||
$q = $db->prepare('UPDATE users SET isadmin = ? WHERE emailaddress = ?');
|
||||
$q->bindValue(1, (int)(bool)$isadmin, SQLITE3_INTEGER);
|
||||
$q->bindValue(2, $username, SQLITE3_TEXT);
|
||||
}
|
||||
$ret = $q->execute();
|
||||
$db->close();
|
||||
|
||||
|
@ -96,6 +122,10 @@ function delete_user($id) {
|
|||
return $ret;
|
||||
}
|
||||
|
||||
function valid_user($name) {
|
||||
return ( bool ) preg_match( "/^[a-z0-9@_.-]+$/i" , $name );
|
||||
}
|
||||
|
||||
function jtable_respond($records, $method = 'multiple', $msg = 'Undefined errormessage') {
|
||||
$jTableResult = array();
|
||||
if ($method == 'error') {
|
||||
|
@ -119,13 +149,31 @@ function jtable_respond($records, $method = 'multiple', $msg = 'Undefined errorm
|
|||
$jTableResult['RecordCount'] = count($records);
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
print json_encode($jTableResult);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
function valid_user($name) {
|
||||
return ( bool ) preg_match( "/^[a-z0-9@_.-]+$/i" , $name );
|
||||
function user_template_list() {
|
||||
global $templates;
|
||||
|
||||
$templatelist = array();
|
||||
foreach ($templates as $template) {
|
||||
if (is_adminuser()
|
||||
or (isset($template['owner'])
|
||||
and ($template['owner'] == get_sess_user() or $template['owner'] == 'public'))) {
|
||||
array_push($templatelist, $template);
|
||||
}
|
||||
}
|
||||
return $templatelist;
|
||||
}
|
||||
|
||||
function user_template_names() {
|
||||
$templatenames = array('None' => 'None');
|
||||
foreach (user_template_list() as $template) {
|
||||
$templatenames[$template['name']] = $template['name'];
|
||||
}
|
||||
return $templatenames;
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -4,102 +4,286 @@ include_once('config.inc.php');
|
|||
include_once('misc.inc.php');
|
||||
include_once('wefactauth.inc.php');
|
||||
|
||||
session_start();
|
||||
global $current_user;
|
||||
|
||||
function is_logged_in() {
|
||||
if (isset($_SESSION['logged_in']) && $_SESSION['logged_in'] == "true") {
|
||||
return TRUE;
|
||||
$current_user = false;
|
||||
|
||||
// session startup
|
||||
function _set_current_user($username, $is_admin = false, $has_csrf_token = false, $is_api = false) {
|
||||
global $current_user;
|
||||
|
||||
$current_user = array(
|
||||
'username' => $username,
|
||||
'is_admin' => $is_admin,
|
||||
'has_csrf_token' => $has_csrf_token,
|
||||
'is_api' => $is_api,
|
||||
);
|
||||
}
|
||||
|
||||
function _check_csrf_token($user) {
|
||||
global $secret;
|
||||
|
||||
if (isset($_SERVER['HTTP_X_CSRF_TOKEN']) && $_SERVER['HTTP_X_CSRF_TOKEN']) {
|
||||
$found_token = $_SERVER['HTTP_X_CSRF_TOKEN'];
|
||||
} elseif (isset($_POST['csrf-token']) && $_POST['csrf-token']) {
|
||||
$found_token = $_POST['csrf-token'];
|
||||
} else {
|
||||
global $adminapikey;
|
||||
global $adminapiips;
|
||||
$found_token = '';
|
||||
}
|
||||
|
||||
if (isset($adminapikey) && isset($adminapiips)) {
|
||||
if (array_search($_SERVER['REMOTE_ADDR'], $adminapiips) !== FALSE) {
|
||||
if ($_POST['adminapikey'] == $adminapikey) {
|
||||
# Allow this request, fake that we're logged in.
|
||||
set_logged_in('admin');
|
||||
set_is_adminuser();
|
||||
$_SESSION['apientrance'] = 'true';
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
if (isset($secret) && $secret) {
|
||||
# if we have a secret keep csrf-token valid across logins
|
||||
$csrf_hmac_secret = hash_pbkdf2('sha256', 'csrf_hmac', $secret, 100, 0, true);
|
||||
$userinfo = base64_encode($user['emailaddress']) . ':' . base64_encode($user['password']);
|
||||
$csrf_token = base64_encode(hash_hmac('sha256', $userinfo, $csrf_hmac_secret, true));
|
||||
} else {
|
||||
# without secret create new token for each session
|
||||
if (!isset($_SESSION['csrf_token'])) {
|
||||
$_SESSION['csrf_token'] = base64_encode(openssl_random_pseudo_bytes(32));
|
||||
}
|
||||
return FALSE;
|
||||
$csrf_token = $_SESSION['csrf_token'];
|
||||
}
|
||||
}
|
||||
|
||||
function set_apiuser() {
|
||||
$_SESSION['apientrance'] = 'true';
|
||||
}
|
||||
|
||||
function is_apiuser() {
|
||||
if (isset($_SESSION['apientrance']) && $_SESSION['apientrance'] = 'true') {
|
||||
return TRUE;
|
||||
if ($found_token === $csrf_token) {
|
||||
global $current_user;
|
||||
$current_user['has_csrf_token'] = true;
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
define('CSRF_TOKEN', $csrf_token);
|
||||
header("X-CSRF-Token: ${csrf_token}");
|
||||
}
|
||||
|
||||
function set_logged_in($login_user) {
|
||||
$_SESSION['logged_in'] = 'true';
|
||||
$_SESSION['username'] = $login_user;
|
||||
}
|
||||
function enc_secret($message) {
|
||||
global $secret;
|
||||
|
||||
function set_is_adminuser() {
|
||||
$_SESSION['is_adminuser'] = 'true';
|
||||
}
|
||||
if (isset($secret) && $secret) {
|
||||
$enc_secret = hash_pbkdf2('sha256', 'encryption', $secret, 100, 0, true);
|
||||
$hmac_secret = hash_pbkdf2('sha256', 'encryption_hmac', $secret, 100, 0, true);
|
||||
|
||||
function is_adminuser() {
|
||||
if (isset($_SESSION['is_adminuser']) && $_SESSION['is_adminuser'] == 'true') {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
$mcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, '') or die('missing mcrypt');
|
||||
|
||||
# add PKCS#7 padding
|
||||
$blocksize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
|
||||
$padlength = $blocksize - (strlen($message) % $blocksize);
|
||||
$message .= str_repeat(chr($padlength), $padlength);
|
||||
|
||||
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
|
||||
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
|
||||
$ciphertext = $iv . mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $enc_secret, $message, MCRYPT_MODE_CBC, $iv);
|
||||
mcrypt_module_close($mcrypt);
|
||||
|
||||
$mac = hash_hmac('sha256', $ciphertext, $hmac_secret, true);
|
||||
return 'enc:' . base64_encode($ciphertext) . ':' . base64_encode($mac);
|
||||
}
|
||||
|
||||
return base64_encode($message);
|
||||
}
|
||||
|
||||
function get_sess_user() {
|
||||
return $_SESSION['username'];
|
||||
function dec_secret($code) {
|
||||
global $secret;
|
||||
$is_encrypted = (substr($code, 0, 4) === 'enc:');
|
||||
if (isset($secret) && $secret) {
|
||||
if (!$is_encrypted) return false;
|
||||
|
||||
$msg = explode(':', $code);
|
||||
if (3 != count($msg)) return false;
|
||||
|
||||
$enc_secret = hash_pbkdf2('sha256', 'encryption', $secret, 100, 0, true);
|
||||
$hmac_secret = hash_pbkdf2('sha256', 'encryption_hmac', $secret, 100, 0, true);
|
||||
|
||||
$msg[1] = base64_decode($msg[1]);
|
||||
$msg[2] = base64_decode($msg[2]);
|
||||
|
||||
$mac = hash_hmac('sha256', $msg[1], $hmac_secret, true);
|
||||
# compare hashes first: this should prevent any timing leak
|
||||
if (hash('sha256', $mac, true) !== hash('sha256', $msg[2], true)) return false;
|
||||
if ($mac !== $msg[2]) return false;
|
||||
|
||||
$mcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, '') or die('missing mcrypt');
|
||||
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
|
||||
$iv = substr($msg[1], 0, $iv_size);
|
||||
$ciphertext = substr($msg[1], $iv_size);
|
||||
$plaintext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $enc_secret, $ciphertext, MCRYPT_MODE_CBC, $iv);
|
||||
mcrypt_module_close($mcrypt);
|
||||
|
||||
# remove PKCS#7 padding
|
||||
$len = strlen($plaintext);
|
||||
$padlength = ord($plaintext[$len-1]);
|
||||
$plaintext = substr($plaintext, 0, $len - $padlength);
|
||||
|
||||
return $plaintext;
|
||||
}
|
||||
|
||||
if ($is_encrypted) return false;
|
||||
return base64_decode($code);
|
||||
}
|
||||
|
||||
function logout() {
|
||||
session_destroy();
|
||||
function _unset_cookie($name) {
|
||||
$is_ssl = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off';
|
||||
setcookie($name, null, -1, null, null, $is_ssl);
|
||||
}
|
||||
|
||||
function _store_auto_login($value) {
|
||||
$is_ssl = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off';
|
||||
// set for 30 days
|
||||
setcookie('NSEDIT_AUTOLOGIN', $value, time()+60*60*24*30, null, null, $is_ssl);
|
||||
}
|
||||
|
||||
function try_login() {
|
||||
global $wefactapiurl;
|
||||
global $wefactapikey;
|
||||
|
||||
if (isset($_POST['username']) and isset($_POST['password'])) {
|
||||
if (valid_user($_POST['username']) === FALSE) {
|
||||
return FALSE;
|
||||
}
|
||||
$do_local_auth = 1;
|
||||
if (_try_login($_POST['username'], $_POST['password'])) {
|
||||
global $secret;
|
||||
|
||||
if (isset($wefactapiurl) && isset($wefactapikey)) {
|
||||
$wefact = do_wefact_auth($_POST['username'], $_POST['password']);
|
||||
if ($wefact === FALSE) {
|
||||
return FALSE;
|
||||
}
|
||||
if ($wefact !== -1) {
|
||||
$do_local_auth = 0;
|
||||
# only store if we have a secret.
|
||||
if ($secret && isset($_POST['autologin']) && $_POST['autologin']) {
|
||||
_store_auto_login(enc_secret(json_encode(array(
|
||||
'username' => $_POST['username'],
|
||||
'password' => $_POST['password']))));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($do_local_auth == 1) {
|
||||
if (do_db_auth($_POST['username'], $_POST['password']) === FALSE) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
$userinfo = get_user_info($_POST['username']);
|
||||
|
||||
set_logged_in($_POST['username']);
|
||||
if (isset($userinfo['isadmin']) && $userinfo['isadmin'] == 1) {
|
||||
set_is_adminuser();
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
?>
|
||||
function _try_login($username, $password) {
|
||||
global $wefactapiurl, $wefactapikey;
|
||||
|
||||
if (!valid_user($username)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$do_local_auth = true;
|
||||
|
||||
if (isset($wefactapiurl) && isset($wefactapikey)) {
|
||||
$wefact = do_wefact_auth($username, $password);
|
||||
if (false === $wefact ) {
|
||||
return false;
|
||||
}
|
||||
if (-1 !== $wefact) {
|
||||
$do_local_auth = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($do_local_auth && !do_db_auth($username, $password)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$user = get_user_info($username);
|
||||
if (!$user) {
|
||||
return false;
|
||||
} else {
|
||||
_set_current_user($username, (bool) $user['isadmin']);
|
||||
|
||||
if (session_id()) {
|
||||
session_unset();
|
||||
session_destroy();
|
||||
}
|
||||
session_start() or die('session failure: could not start session');
|
||||
session_regenerate_id(true) or die('session failure: regenerated id failed');
|
||||
session_unset();
|
||||
$_SESSION['username'] = $username;
|
||||
|
||||
# requires session:
|
||||
_check_csrf_token($user);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function _check_session() {
|
||||
global $adminapikey, $adminapiips;
|
||||
|
||||
$is_ssl = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off';
|
||||
session_set_cookie_params(30*60, null, null, $is_ssl, true);
|
||||
session_name('NSEDIT_SESSION');
|
||||
|
||||
if (isset($adminapikey) && '' !== $adminapikey && isset($adminapiips) && isset($_POST['adminapikey'])) {
|
||||
if (false !== array_search($_SERVER['REMOTE_ADDR'], $adminapiips)
|
||||
and $_POST['adminapikey'] === $adminapikey)
|
||||
{
|
||||
# Allow this request, fake that we're logged in as user.
|
||||
return _set_current_user('admin', true, true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
header('Status: 403 Forbidden');
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_COOKIE['NSEDIT_SESSION'])) {
|
||||
if (session_start() && isset($_SESSION['username'])) {
|
||||
$user = get_user_info($_SESSION['username']);
|
||||
if (!$user) {
|
||||
session_destroy();
|
||||
session_unset();
|
||||
} else {
|
||||
_set_current_user($_SESSION['username'], (bool) $user['isadmin']);
|
||||
_check_csrf_token($user);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// failed: remove cookie
|
||||
_unset_cookie('NSEDIT_SESSION');
|
||||
}
|
||||
|
||||
if (isset($_COOKIE['NSEDIT_AUTOLOGIN'])) {
|
||||
$login = json_decode(dec_secret($_COOKIE['NSEDIT_AUTOLOGIN']), 1);
|
||||
if ($login and isset($login['username']) and isset($login['password'])
|
||||
and _try_login($login['username'], $login['password'])) {
|
||||
_store_auto_login($_COOKIE['NSEDIT_AUTOLOGIN']); # reset cookie
|
||||
return;
|
||||
}
|
||||
|
||||
// failed: remove cookie
|
||||
_unset_cookie('NSEDIT_AUTOLOGIN');
|
||||
}
|
||||
}
|
||||
|
||||
# auto load session if possible
|
||||
_check_session();
|
||||
|
||||
function is_logged_in() {
|
||||
global $current_user;
|
||||
return (bool) $current_user;
|
||||
}
|
||||
|
||||
# GET/HEAD requests only require a logged in user (they shouldn't trigger any
|
||||
# "writes"); all other requests require the X-CSRF-Token to be present.
|
||||
function is_csrf_safe() {
|
||||
global $current_user;
|
||||
|
||||
switch ($_SERVER['REQUEST_METHOD']) {
|
||||
case 'GET':
|
||||
case 'HEAD':
|
||||
return is_logged_in();
|
||||
default:
|
||||
return (bool) $current_user && (bool) $current_user['has_csrf_token'];
|
||||
}
|
||||
}
|
||||
|
||||
function is_apiuser() {
|
||||
global $current_user;
|
||||
return $current_user && (bool) $current_user['is_api'];
|
||||
}
|
||||
|
||||
function is_adminuser() {
|
||||
global $current_user;
|
||||
return $current_user && (bool) $current_user['is_admin'];
|
||||
}
|
||||
|
||||
function get_sess_user() {
|
||||
global $current_user;
|
||||
return $current_user ? $current_user['username'] : null;
|
||||
}
|
||||
|
||||
function logout() {
|
||||
@session_destroy();
|
||||
@session_unset();
|
||||
if (isset($_COOKIE['NSEDIT_AUTOLOGIN'])) {
|
||||
_unset_cookie('NSEDIT_AUTOLOGIN');
|
||||
}
|
||||
if (isset($_COOKIE['NSEDIT_SESSION'])) {
|
||||
_unset_cookie('NSEDIT_SESSION');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,5 +78,3 @@ function do_wefact_auth($u, $p) {
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
336
index.php
336
index.php
|
@ -7,12 +7,11 @@ include_once('includes/misc.inc.php');
|
|||
if (isset($_GET['logout']) or isset($_POST['logout'])) {
|
||||
logout();
|
||||
header("Location: index.php");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!is_logged_in() and isset($_POST['formname']) && $_POST['formname'] == "loginform") {
|
||||
if (try_login() === TRUE) {
|
||||
set_logged_in($_POST['username']);
|
||||
} else {
|
||||
if (!is_logged_in() and isset($_POST['formname']) and $_POST['formname'] === "loginform") {
|
||||
if (!try_login()) {
|
||||
$errormsg = "Error while trying to authenticate you\n";
|
||||
}
|
||||
}
|
||||
|
@ -54,18 +53,28 @@ if (!is_logged_in()) {
|
|||
<table>
|
||||
<tr>
|
||||
<td class="label">Username:</td>
|
||||
<td><input id="username" type="text" name="username"/></td>
|
||||
<td><input id="username" type="text" name="username"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Password:</td>
|
||||
<td><input type="password" name="password"/></td>
|
||||
<td><input type="password" name="password"></td>
|
||||
</tr>
|
||||
<?php
|
||||
if (isset($secret) && $secret) {
|
||||
?>
|
||||
<tr>
|
||||
<td class="label">Remember me:</td>
|
||||
<td><input type="checkbox" name="autologin" value="1"></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><input type="submit" name="submit" value="Log me in!"/></td>
|
||||
<td><input type="submit" name="submit" value="Log me in!"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<input type="hidden" name="formname" value="loginform"/>
|
||||
<input type="hidden" name="formname" value="loginform">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -76,19 +85,6 @@ if (!is_logged_in()) {
|
|||
exit(0);
|
||||
}
|
||||
|
||||
foreach ($templates as $template) {
|
||||
if (is_adminuser() or (isset($template['owner']) && $template['owner'] == get_sess_user()) or ($template['owner'] == 'public')) {
|
||||
$templatelist[] = "'" . $template['name'] . "':'" . $template['name'] . "'";
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($templatelist)) {
|
||||
$tmpllist = ',';
|
||||
$tmpllist .= join(',', $templatelist);
|
||||
} else {
|
||||
$tmpllist = '';
|
||||
}
|
||||
|
||||
?>
|
||||
<body>
|
||||
<div id="wrap">
|
||||
|
@ -126,21 +122,60 @@ if (isset($templatelist)) {
|
|||
<? } ?>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var entityMap = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
"/": '/'
|
||||
};
|
||||
window.csrf_token = '<?php echo CSRF_TOKEN ?>';
|
||||
|
||||
function escapeHtml(string) {
|
||||
return String(string).replace(/[&<>"'\/]/g, function (s) {
|
||||
return entityMap[s];
|
||||
$(document).ready(function () {
|
||||
function csrfSafeMethod(method) {
|
||||
// these HTTP methods do not require CSRF protection
|
||||
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
|
||||
}
|
||||
$.ajaxSetup({
|
||||
beforeSend: function(xhr, settings) {
|
||||
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
|
||||
xhr.setRequestHeader("X-CSRF-Token", window.csrf_token);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
function displayDnssecIcon(zone) {
|
||||
if (zone.record.dnssec == true) {
|
||||
var $img = $('<img class="list" src="img/lock.png" title="DNSSec Info" />');
|
||||
$img.click(function () {
|
||||
$("#dnssecinfo").html("");
|
||||
$.each(zone.record.keyinfo, function ( i, val) {
|
||||
if (val.dstxt) {
|
||||
$("#dnssecinfo").append("<p><h2>"+val.keytype+"</h2><pre>"+val.dstxt+"</pre></p>");
|
||||
}
|
||||
});
|
||||
$("#dnssecinfo").dialog({
|
||||
modal: true,
|
||||
title: "DS-records for "+zone.record.name,
|
||||
width: 'auto',
|
||||
buttons: {
|
||||
Ok: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return $img;
|
||||
} else {
|
||||
return '<img src="img/lock_open.png" title="DNSSec Disabled" />';
|
||||
}
|
||||
}
|
||||
|
||||
function displayContent(fieldName) {
|
||||
return function(data) {
|
||||
var value = data.record[fieldName];
|
||||
switch (fieldName) {
|
||||
case 'priority':
|
||||
value = (value === 0) ? '' : value;
|
||||
break;
|
||||
}
|
||||
return $('<span>').text(value);
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
$('#SlaveZones').jtable({
|
||||
|
@ -155,9 +190,10 @@ $(document).ready(function () {
|
|||
openChildAsAccordion: true,
|
||||
actions: {
|
||||
listAction: 'zones.php?action=listslaves',
|
||||
updateAction: 'zones.php?action=update',
|
||||
<? if (is_adminuser() or $allowzoneadd === TRUE) { ?>
|
||||
createAction: 'zones.php?action=create',
|
||||
deleteAction: 'zones.php?action=delete'
|
||||
deleteAction: 'zones.php?action=delete',
|
||||
<? } ?>
|
||||
},
|
||||
fields: {
|
||||
|
@ -166,15 +202,32 @@ $(document).ready(function () {
|
|||
type: 'hidden'
|
||||
},
|
||||
name: {
|
||||
title: 'Domain'
|
||||
title: 'Domain',
|
||||
width: '8%',
|
||||
display: displayContent('name'),
|
||||
edit: false,
|
||||
inputClass: 'domain',
|
||||
listClass: 'domain'
|
||||
},
|
||||
dnssec: {
|
||||
title: 'DNSSEC',
|
||||
width: '3%',
|
||||
create: false,
|
||||
edit: false,
|
||||
display: displayDnssecIcon,
|
||||
listClass: 'dnssec'
|
||||
},
|
||||
<? if (is_adminuser()) { ?>
|
||||
owner: {
|
||||
title: 'Owner',
|
||||
width: '8%',
|
||||
display: displayContent('owner'),
|
||||
options: function(data) {
|
||||
return 'users.php?action=listoptions';
|
||||
},
|
||||
defaultValue: 'admin'
|
||||
defaultValue: 'admin',
|
||||
inputClass: 'owner',
|
||||
listClass: 'owner'
|
||||
},
|
||||
<? } ?>
|
||||
kind: {
|
||||
|
@ -183,9 +236,30 @@ $(document).ready(function () {
|
|||
list: false,
|
||||
defaultValue: 'Slave'
|
||||
},
|
||||
masters: {
|
||||
title: 'Masters',
|
||||
width: '20%',
|
||||
display: function(data) {
|
||||
return $('<span>').text(data.record.masters.join('\n'));
|
||||
},
|
||||
input: function(data) {
|
||||
var elem = $('<input type="text" name="masters">');
|
||||
if (data && data.record) {
|
||||
elem.attr('value', data.record.masters.join(','));
|
||||
}
|
||||
return elem;
|
||||
},
|
||||
inputClass: 'masters',
|
||||
listClass: 'masters'
|
||||
},
|
||||
serial: {
|
||||
title: 'Serial',
|
||||
create: false
|
||||
width: '10%',
|
||||
display: displayContent('serial'),
|
||||
create: false,
|
||||
edit: false,
|
||||
inputClass: 'serial',
|
||||
listClass: 'serial'
|
||||
},
|
||||
records: {
|
||||
width: '5%',
|
||||
|
@ -206,22 +280,34 @@ $(document).ready(function () {
|
|||
},
|
||||
fields: {
|
||||
name: {
|
||||
title: 'Label'
|
||||
title: 'Label',
|
||||
width: '7%',
|
||||
display: displayContent('name'),
|
||||
listClass: 'name'
|
||||
},
|
||||
type: {
|
||||
title: 'Type'
|
||||
title: 'Type',
|
||||
width: '2%',
|
||||
display: displayContent('type'),
|
||||
listClass: 'type'
|
||||
},
|
||||
prio: {
|
||||
title: 'Prio'
|
||||
priority: {
|
||||
title: 'Prio',
|
||||
width: '1%',
|
||||
display: displayContent('priority'),
|
||||
listClass: 'priority'
|
||||
},
|
||||
content: {
|
||||
title: 'Content',
|
||||
display: function (data) {
|
||||
return escapeHtml(data.record.content);
|
||||
}
|
||||
width: '30%',
|
||||
display: displayContent('content'),
|
||||
listClass: 'content'
|
||||
},
|
||||
ttl: {
|
||||
title: 'TTL'
|
||||
title: 'TTL',
|
||||
width: '2%',
|
||||
display: displayContent('ttl'),
|
||||
listClass: 'ttl'
|
||||
}
|
||||
}
|
||||
}, function (data) {
|
||||
|
@ -273,79 +359,76 @@ $(document).ready(function () {
|
|||
type: 'hidden'
|
||||
},
|
||||
name: {
|
||||
title: 'Domain'
|
||||
title: 'Domain',
|
||||
width: '8%',
|
||||
display: displayContent('name'),
|
||||
edit: false,
|
||||
inputClass: 'domain',
|
||||
listClass: 'domain'
|
||||
},
|
||||
dnssec: {
|
||||
title: 'DNSSEC',
|
||||
width: '3%',
|
||||
create: false,
|
||||
edit: false,
|
||||
display: function (zone) {
|
||||
if (zone.record.dnssec == true) {
|
||||
var $img = $('<img class="list" src="img/lock.png" title="DNSSec Info" />');
|
||||
$img.click(function () {
|
||||
$("#dnssecinfo").html("");
|
||||
$.each(zone.record.keyinfo, function ( i, val) {
|
||||
if (val.dstxt) {
|
||||
$("#dnssecinfo").append("<p><h2>"+val.keytype+"</h2><pre>"+val.dstxt+"</pre></p>");
|
||||
}
|
||||
});
|
||||
$("#dnssecinfo").dialog({
|
||||
modal: true,
|
||||
title: "DS-records for "+zone.record.name,
|
||||
width: 'auto',
|
||||
buttons: {
|
||||
Ok: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return $img;
|
||||
} else {
|
||||
return '<img src="img/lock_open.png" title="DNSSec Disabled" />';
|
||||
}
|
||||
}
|
||||
display: displayDnssecIcon,
|
||||
listClass: 'dnssec'
|
||||
},
|
||||
<? if (is_adminuser()) { ?>
|
||||
owner: {
|
||||
title: 'Owner',
|
||||
width: '8%',
|
||||
display: displayContent('owner'),
|
||||
options: function(data) {
|
||||
return 'users.php?action=listoptions';
|
||||
},
|
||||
defaultValue: 'admin'
|
||||
defaultValue: 'admin',
|
||||
inputClass: 'owner',
|
||||
listClass: 'owner'
|
||||
},
|
||||
<? } ?>
|
||||
kind: {
|
||||
title: 'Type',
|
||||
width: '20%',
|
||||
display: displayContent('kind'),
|
||||
options: {'Native': 'Native', 'Master': 'Master'},
|
||||
defaultValue: '<? echo $defaults['defaulttype']; ?>',
|
||||
edit: false
|
||||
edit: false,
|
||||
inputClass: 'kind',
|
||||
listClass: 'kind'
|
||||
},
|
||||
template: {
|
||||
title: 'Template',
|
||||
options: {'None': 'None'<? echo $tmpllist; ?>},
|
||||
options: <? echo json_encode(user_template_names()); ?>,
|
||||
list: false,
|
||||
create: true,
|
||||
edit: false
|
||||
edit: false,
|
||||
inputClass: 'template'
|
||||
},
|
||||
nameserver1: {
|
||||
title: 'Pri. Nameserver',
|
||||
create: true,
|
||||
list: false,
|
||||
edit: false,
|
||||
defaultValue: '<? echo $defaults['primaryns']; ?>'
|
||||
defaultValue: '<? echo $defaults['primaryns']; ?>',
|
||||
inputClass: 'nameserver nameserver1'
|
||||
},
|
||||
nameserver2: {
|
||||
title: 'Sec. Nameserver',
|
||||
create: true,
|
||||
list: false,
|
||||
edit: false,
|
||||
defaultValue: '<? echo $defaults['secondaryns']; ?>'
|
||||
defaultValue: '<? echo $defaults['secondaryns']; ?>',
|
||||
inputClass: 'nameserver nameserver2'
|
||||
},
|
||||
serial: {
|
||||
title: 'Serial',
|
||||
width: '10%',
|
||||
display: displayContent('serial'),
|
||||
create: false,
|
||||
edit: false
|
||||
edit: false,
|
||||
inputClass: 'serial',
|
||||
listClass: 'serial'
|
||||
},
|
||||
records: {
|
||||
width: '5%',
|
||||
|
@ -379,6 +462,7 @@ $(document).ready(function () {
|
|||
},
|
||||
id: {
|
||||
key: true,
|
||||
type: 'hidden',
|
||||
create: false,
|
||||
edit: false,
|
||||
list: false
|
||||
|
@ -390,10 +474,17 @@ $(document).ready(function () {
|
|||
},
|
||||
name: {
|
||||
title: 'Label',
|
||||
create: true
|
||||
width: '7%',
|
||||
create: true,
|
||||
display: displayContent('name'),
|
||||
inputClass: 'name',
|
||||
listClass: 'name'
|
||||
},
|
||||
type: {
|
||||
title: 'Type',
|
||||
width: '2%',
|
||||
options: function() {
|
||||
/*
|
||||
zonename = new String(zone.record.name);
|
||||
if (zonename.match(/(\.in-addr|\.ip6)\.arpa/)) {
|
||||
return {
|
||||
|
@ -403,38 +494,53 @@ $(document).ready(function () {
|
|||
'TXT':'TXT',
|
||||
'SOA':'SOA'
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'AAAA': 'AAAA',
|
||||
'A': 'A',
|
||||
'CNAME': 'CNAME',
|
||||
'MX': 'MX',
|
||||
'PTR': 'PTR',
|
||||
'SRV': 'SRV',
|
||||
'TXT': 'TXT',
|
||||
'NS': 'NS',
|
||||
'SOA': 'SOA'
|
||||
};
|
||||
}
|
||||
*/
|
||||
return {
|
||||
'A': 'A',
|
||||
'AAAA': 'AAAA',
|
||||
'CNAME': 'CNAME',
|
||||
'MX': 'MX',
|
||||
'NAPTR': 'NAPTR',
|
||||
'NS': 'NS',
|
||||
'PTR': 'PTR',
|
||||
'SOA': 'SOA',
|
||||
'SPF': 'SPF',
|
||||
'SRV': 'SRV',
|
||||
'TLSA': 'TLSA',
|
||||
'TXT': 'TXT',
|
||||
};
|
||||
},
|
||||
create: true
|
||||
display: displayContent('type'),
|
||||
create: true,
|
||||
inputClass: 'type',
|
||||
listClass: 'type'
|
||||
},
|
||||
priority: {
|
||||
title: 'Prio',
|
||||
width: '1%',
|
||||
create: true,
|
||||
defaultValue: '<? echo $defaults['priority']; ?>'
|
||||
display: displayContent('priority'),
|
||||
defaultValue: '<? echo $defaults['priority']; ?>',
|
||||
inputClass: 'priority',
|
||||
listClass: 'priority'
|
||||
},
|
||||
content: {
|
||||
title: 'Content',
|
||||
width: '30%',
|
||||
create: true,
|
||||
display: function (data) {
|
||||
return escapeHtml(data.record.content);
|
||||
}
|
||||
display: displayContent('content'),
|
||||
inputClass: 'content',
|
||||
listClass: 'content'
|
||||
},
|
||||
ttl: {
|
||||
title: 'TTL',
|
||||
width: '2%',
|
||||
create: true,
|
||||
defaultValue: '<? echo $defaults['ttl']; ?>'
|
||||
display: displayContent('ttl'),
|
||||
defaultValue: '<? echo $defaults['ttl']; ?>',
|
||||
inputClass: 'ttl',
|
||||
listClass: 'ttl'
|
||||
}
|
||||
}
|
||||
}, function (data) {
|
||||
|
@ -457,7 +563,8 @@ $(document).ready(function () {
|
|||
type: 'hidden'
|
||||
},
|
||||
name: {
|
||||
title: 'Domain'
|
||||
title: 'Domain',
|
||||
inputClass: 'domain'
|
||||
},
|
||||
<? if (is_adminuser()) { ?>
|
||||
owner: {
|
||||
|
@ -465,38 +572,44 @@ $(document).ready(function () {
|
|||
options: function(data) {
|
||||
return 'users.php?action=listoptions';
|
||||
},
|
||||
defaultValue: 'admin'
|
||||
defaultValue: 'admin',
|
||||
inputClass: 'owner'
|
||||
},
|
||||
<? } ?>
|
||||
kind: {
|
||||
title: 'Type',
|
||||
options: {'Native': 'Native', 'Master': 'Master'},
|
||||
defaultValue: '<? echo $defaults['defaulttype']; ?>',
|
||||
edit: false
|
||||
edit: false,
|
||||
inputClass: 'type'
|
||||
},
|
||||
zone: {
|
||||
title: 'Zonedata',
|
||||
type: 'textarea'
|
||||
type: 'textarea',
|
||||
inputClass: 'zonedata'
|
||||
},
|
||||
owns: {
|
||||
title: 'Overwrite Nameservers',
|
||||
type: 'checkbox',
|
||||
values: {'0': 'No', '1': 'Yes'},
|
||||
defaultValue: 1
|
||||
defaultValue: 1,
|
||||
inputClass: 'overwrite_namerserver'
|
||||
},
|
||||
nameserver1: {
|
||||
title: 'Pri. Nameserver',
|
||||
create: true,
|
||||
list: false,
|
||||
edit: false,
|
||||
defaultValue: '<? echo $defaults['primaryns']; ?>'
|
||||
defaultValue: '<? echo $defaults['primaryns']; ?>',
|
||||
inputClass: 'nameserver nameserver1'
|
||||
},
|
||||
nameserver2: {
|
||||
title: 'Sec. Nameserver',
|
||||
create: true,
|
||||
list: false,
|
||||
edit: false,
|
||||
defaultValue: '<? echo $defaults['secondaryns']; ?>'
|
||||
defaultValue: '<? echo $defaults['secondaryns']; ?>',
|
||||
inputClass: 'nameserver nameserver2'
|
||||
},
|
||||
},
|
||||
recordAdded: function() {
|
||||
|
@ -549,17 +662,23 @@ $(document).ready(function () {
|
|||
type: 'hidden'
|
||||
},
|
||||
emailaddress: {
|
||||
title: 'User'
|
||||
title: 'User',
|
||||
display: displayContent('emailaddress'),
|
||||
inputClass: 'emailaddress',
|
||||
listClass: 'emailaddress'
|
||||
},
|
||||
password: {
|
||||
title: 'Password',
|
||||
type: 'password',
|
||||
list: false
|
||||
list: false,
|
||||
inputClass: 'password',
|
||||
},
|
||||
isadmin: {
|
||||
title: 'Admin',
|
||||
type: 'checkbox',
|
||||
values: {'0': 'No', '1': 'Yes'}
|
||||
values: {'0': 'No', '1': 'Yes'},
|
||||
inputClass: 'isadmin',
|
||||
listClass: 'isadmin'
|
||||
}
|
||||
},
|
||||
recordAdded: function() {
|
||||
|
@ -575,4 +694,3 @@ $(document).ready(function () {
|
|||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
86
users.php
86
users.php
|
@ -4,49 +4,95 @@ include_once('includes/config.inc.php');
|
|||
include_once('includes/session.inc.php');
|
||||
include_once('includes/misc.inc.php');
|
||||
|
||||
if (!is_logged_in()) {
|
||||
header("Location: index.php");
|
||||
if (!is_csrf_safe()) {
|
||||
header('Status: 403');
|
||||
header('Location: ./index.php');
|
||||
jtable_respond(null, 'error', "Authentication required");
|
||||
}
|
||||
|
||||
if (!is_adminuser()) {
|
||||
header('Status: 403');
|
||||
jtable_respond(null, 'error', "You need adminprivileges to get here");
|
||||
}
|
||||
|
||||
if (isset($_GET['action'])) {
|
||||
$action = $_GET['action'];
|
||||
} else {
|
||||
if (!isset($_GET['action'])) {
|
||||
header('Status: 400');
|
||||
jtable_respond(null, 'error', 'No action given');
|
||||
}
|
||||
|
||||
if ($action == "list") {
|
||||
switch ($_GET['action']) {
|
||||
|
||||
case "list":
|
||||
$users = get_all_users();
|
||||
jtable_respond($users);
|
||||
} elseif ($action == "listoptions") {
|
||||
break;
|
||||
|
||||
case "listoptions":
|
||||
$users = get_all_users();
|
||||
$retusers = array();
|
||||
foreach ($users as $user) {
|
||||
$retusers[] = array (
|
||||
$retusers[] = array(
|
||||
'DisplayText' => $user['emailaddress'],
|
||||
'Value' => $user['emailaddress']);
|
||||
}
|
||||
jtable_respond($retusers, 'options');
|
||||
} elseif ($action == "create" or $action == "update") {
|
||||
if (valid_user($_POST['emailaddress']) === FALSE) {
|
||||
break;
|
||||
|
||||
case "create":
|
||||
$emailaddress = isset($_POST['emailaddress']) ? $_POST['emailaddress'] : '';
|
||||
$isadmin = isset($_POST['isadmin']) ? $_POST['isadmin'] : '0';
|
||||
$password = isset($_POST['password']) ? $_POST['password'] : '';
|
||||
|
||||
if (!valid_user($emailaddress)) {
|
||||
jtable_respond(null, 'error', "Please only use ^[a-z0-9@_.-]+$ for usernames");
|
||||
}
|
||||
$isadmin = $_POST['isadmin'] ? $_POST['isadmin'] : '0';
|
||||
if (add_user($_POST['emailaddress'], $isadmin, $_POST['password']) !== FALSE) {
|
||||
unset($_POST['password']);
|
||||
jtable_respond($_POST, 'single');
|
||||
} else {
|
||||
jtable_respond(null, 'error', 'Could not add/change this user');
|
||||
|
||||
if (!$password) {
|
||||
jtable_respond(null, 'error', 'Cannot create user without password');
|
||||
}
|
||||
} elseif ($action == "delete") {
|
||||
|
||||
if (user_exists($emailaddress)) {
|
||||
jtable_respond(null, 'error', 'User already exists');
|
||||
}
|
||||
|
||||
if (add_user($emailaddress, $isadmin, $password)) {
|
||||
$result = array('emailaddress' => $emailaddress, 'isadmin' => $isadmin);
|
||||
jtable_respond($result, 'single');
|
||||
} else {
|
||||
jtable_respond(null, 'error', 'Could not create user');
|
||||
}
|
||||
break;
|
||||
|
||||
case "update":
|
||||
$emailaddress = isset($_POST['emailaddress']) ? $_POST['emailaddress'] : '';
|
||||
$isadmin = isset($_POST['isadmin']) ? $_POST['isadmin'] : '0';
|
||||
$password = isset($_POST['password']) ? $_POST['password'] : '';
|
||||
|
||||
if (!valid_user($emailaddress)) {
|
||||
jtable_respond(null, 'error', "Please only use ^[a-z0-9@_.-]+$ for usernames");
|
||||
}
|
||||
|
||||
if (!user_exists($emailaddress)) {
|
||||
jtable_respond(null, 'error', 'Cannot update not existing user');
|
||||
}
|
||||
|
||||
if (update_user($emailaddress, $isadmin, $password)) {
|
||||
$result = array('emailaddress' => $emailaddress, 'isadmin' => $isadmin);
|
||||
jtable_respond($result, 'single');
|
||||
} else {
|
||||
jtable_respond(null, 'error', 'Could not update user');
|
||||
}
|
||||
break;
|
||||
|
||||
case "delete":
|
||||
if (delete_user($_POST['id']) !== FALSE) {
|
||||
jtable_respond(null, 'delete');
|
||||
} else {
|
||||
jtable_respond(null, 'error', 'Could not delete this user');
|
||||
jtable_respond(null, 'error', 'Could not delete user');
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
?>
|
||||
default:
|
||||
jtable_respond(null, 'error', 'Invalid action');
|
||||
break;
|
||||
}
|
||||
|
|
722
zones.php
722
zones.php
|
@ -1,105 +1,210 @@
|
|||
<?php
|
||||
|
||||
|
||||
include_once('includes/config.inc.php');
|
||||
include_once('includes/session.inc.php');
|
||||
include_once('includes/misc.inc.php');
|
||||
|
||||
if (!is_logged_in()) {
|
||||
header("Location: index.php");
|
||||
die();
|
||||
if (!is_csrf_safe()) {
|
||||
header('Status: 403');
|
||||
header('Location: ./index.php');
|
||||
jtable_respond(null, 'error', "Authentication required");
|
||||
}
|
||||
|
||||
header("Content-Type: application/json");
|
||||
|
||||
function _do_curl($method, $opts = null, $type = 'post') {
|
||||
function api_request($path, $opts = null, $type = null) {
|
||||
global $apisid, $apiuser, $apipass, $apiip, $apiport;
|
||||
$method = preg_replace('/:serverid:/', $apisid, $method);
|
||||
$method = preg_replace('/^\/+/', '', $method);
|
||||
|
||||
$url = "http://$apiip:$apiport${path}";
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_USERPWD, "$apiuser:$apipass");
|
||||
curl_setopt($ch, CURLOPT_URL, "http://$apiip:$apiport/$method");
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
if ($opts) {
|
||||
if (!$type) {
|
||||
$type = 'POST';
|
||||
}
|
||||
$postdata = json_encode($opts);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
|
||||
}
|
||||
if ($type == 'delete') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
|
||||
}
|
||||
if ($type == 'patch') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
|
||||
switch ($type) {
|
||||
case 'DELETE':
|
||||
case 'PATCH':
|
||||
case 'PUT':
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $type);
|
||||
break;
|
||||
case 'POST':
|
||||
break;
|
||||
}
|
||||
|
||||
$return = curl_exec($ch);
|
||||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$json = json_decode($return, 1);
|
||||
|
||||
if (isset($json['error'])) {
|
||||
jtable_respond(null, 'error', 'API Responds: '.$json['error']);
|
||||
} else {
|
||||
return $return;
|
||||
jtable_respond(null, 'error', "API Error $code: ".$json['error']);
|
||||
} elseif ($code < 200 || $code >= 300) {
|
||||
jtable_respond(null, 'error', "API Error: $code");
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
|
||||
function _valid_label($name) {
|
||||
return ( bool ) preg_match( "/^([-.a-z0-9_\/\*]+)?$/i" , $name );
|
||||
function zones_api_request($opts = null, $type = 'POST') {
|
||||
global $apisid;
|
||||
|
||||
return api_request("/servers/${apisid}/zones", $opts, $type);
|
||||
}
|
||||
|
||||
function _create_record($name, $records, $input, $zoneurl) {
|
||||
global $defaults;
|
||||
function get_all_zones() {
|
||||
return zones_api_request();
|
||||
}
|
||||
|
||||
$content = ($input['type'] == "TXT") ? '"'.$input['content'].'"' : $input['content'];
|
||||
function _get_zone_by_key($key, $value) {
|
||||
if ($value !== '') {
|
||||
foreach (get_all_zones() as $zone) {
|
||||
if ($zone[$key] === $value) {
|
||||
$zone['owner'] = get_zone_owner($zone['name'], 'admin');
|
||||
|
||||
if (_valid_label($input['name']) === FALSE) {
|
||||
jtable_respond(null, 'error', "Please only use [a-z0-9_/.-]");
|
||||
}
|
||||
if (is_ascii($content) === FALSE or is_ascii($input['name']) === FALSE) {
|
||||
jtable_respond(null, 'error', "Please only use ASCII-characters in your fields");
|
||||
if (!check_owner($zone)) {
|
||||
jtable_respond(null, 'error', 'Access denied');
|
||||
}
|
||||
return $zone;
|
||||
}
|
||||
}
|
||||
}
|
||||
header('Status: 404 Not found');
|
||||
jtable_respond(null, 'error', "Zone not found");
|
||||
}
|
||||
|
||||
if (preg_match('/^TXT$/', $input['type'])) {
|
||||
$content = stripslashes($input['content']);
|
||||
$content = preg_replace('/(^"|"$)/', '', $content);
|
||||
$content = addslashes($content);
|
||||
$content = '"'.$content.'"';
|
||||
}
|
||||
function get_zone_by_url($zoneurl) {
|
||||
return _get_zone_by_key('url', $zoneurl);
|
||||
}
|
||||
|
||||
array_push($records, array(
|
||||
'disabled' => false,
|
||||
'type' => $input['type'],
|
||||
'name' => $name,
|
||||
'ttl' => isset($input['ttl']) ? $input['ttl'] : $defaults['ttl'],
|
||||
'priority' => $input['priority'] ? $input['priority'] : $defaults['priority'],
|
||||
'content' => $content));
|
||||
function get_zone_by_id($zoneid) {
|
||||
return _get_zone_by_key('id', $zoneid);
|
||||
}
|
||||
|
||||
$patch = array();
|
||||
$patch['rrsets'] = array();
|
||||
array_push($patch['rrsets'], array(
|
||||
'comments' => array(),
|
||||
'records' => $records,
|
||||
'changetype'=> "REPLACE",
|
||||
'type' => $input['type'],
|
||||
'name' => $name));
|
||||
_do_curl($zoneurl, $patch, 'patch');
|
||||
|
||||
return $records;
|
||||
function get_zone_by_name($zonename) {
|
||||
return _get_zone_by_key('name', $zonename);
|
||||
}
|
||||
|
||||
/* This function is taken from:
|
||||
http://pageconfig.com/post/how-to-validate-ascii-text-in-php and got fixed by
|
||||
#powerdns */
|
||||
|
||||
function is_ascii( $string = '' ) {
|
||||
function is_ascii($string) {
|
||||
return ( bool ) ! preg_match( '/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x80-\\xff]/' , $string );
|
||||
}
|
||||
|
||||
function getrecords_by_name_type($zoneurl, $name, $type) {
|
||||
$zone = json_decode(_do_curl($zoneurl), 1);
|
||||
function _valid_label($name) {
|
||||
return is_ascii($name) && ( bool ) preg_match("/^([-.a-z0-9_\/\*]+)?$/i", $name );
|
||||
}
|
||||
|
||||
function make_record($zone, $input) {
|
||||
global $defaults;
|
||||
|
||||
$name = isset($input['name']) ? $input['name'] : '';
|
||||
|
||||
if ('' == $name) {
|
||||
$name = $zone['name'];
|
||||
} elseif (string_ends_with($name, '.')) {
|
||||
# "absolute" name, shouldn't append zone[name] - but check.
|
||||
$name = substr($name, 0, -1);
|
||||
if (!string_ends_with($name, $zone['name'])) {
|
||||
jtable_respond(null, 'error', "Name $name not in zone ".$zone['name']);
|
||||
}
|
||||
} else if (!string_ends_with($name, $zone['name'])) {
|
||||
$name = $name . '.' . $zone['name'];
|
||||
}
|
||||
|
||||
$ttl = (int) ((isset($input['ttl']) && $input['ttl']) ? $input['ttl'] : $defaults['ttl']);
|
||||
$priority = (int) ((isset($input['priority']) && $input['priority']) ? $input['priority'] : $defaults['priority']);
|
||||
$type = isset($input['type']) ? $input['type'] : '';
|
||||
$disabled = (bool) (isset($input['disabled']) && $input['disabled']);
|
||||
|
||||
$content = isset($input['content']) ? $input['content'] : '';
|
||||
|
||||
if ($type === 'TXT') {
|
||||
# empty TXT records are ok, otherwise require surrounding quotes: "..."
|
||||
if (strlen($content) == 1 || substr($content, 0, 1) !== '"' || substr($content, -1) !== '"') {
|
||||
# fix quoting: first escape all \, then all ", then surround with quotes.
|
||||
$content = '"'.str_replace('"', '\\"', str_replace('\\', '\\\\', $content)).'"';
|
||||
}
|
||||
}
|
||||
|
||||
if (!_valid_label($name)) {
|
||||
jtable_respond(null, 'error', "Please only use [a-z0-9_/.-]");
|
||||
}
|
||||
if (!$type) {
|
||||
jtable_respond(null, 'error', "Require a type");
|
||||
}
|
||||
if (!is_ascii($content)) {
|
||||
jtable_respond(null, 'error', "Please only use ASCII-characters in your fields");
|
||||
}
|
||||
|
||||
return array(
|
||||
'disabled' => $disabled,
|
||||
'type' => $type,
|
||||
'name' => $name,
|
||||
'ttl' => $ttl,
|
||||
'priority' => $priority,
|
||||
'content' => $content);
|
||||
}
|
||||
|
||||
function update_records($zone, $name_and_type, $inputs) {
|
||||
# need one "record" to extract name and type, in case we have no inputs
|
||||
# (deletion of all records)
|
||||
$name_and_type = make_record($zone, $name_and_type);
|
||||
$name = $name_and_type['name'];
|
||||
$type = $name_and_type['type'];
|
||||
|
||||
$records = array();
|
||||
foreach ($inputs as $input) {
|
||||
$record = make_record($zone, $input);
|
||||
if ($record['name'] !== $name || $record['type'] !== $type) {
|
||||
jtable_respond(null, 'error', "Records not matching");
|
||||
}
|
||||
|
||||
array_push($records, $record);
|
||||
}
|
||||
|
||||
if (!_valid_label($name)) {
|
||||
jtable_respond(null, 'error', "Please only use [a-z0-9_/.-]");
|
||||
}
|
||||
|
||||
$patch = array(
|
||||
'rrsets' => array(array(
|
||||
'name' => $name,
|
||||
'type' => $type,
|
||||
'changetype' => count($records) ? 'REPLACE' : 'DELETE',
|
||||
'records' => $records)));
|
||||
|
||||
api_request($zone['url'], $patch, 'PATCH');
|
||||
}
|
||||
|
||||
function create_record($zone, $input) {
|
||||
$record = make_record($zone, $input);
|
||||
$records = get_records_by_name_type($zone, $record['name'], $record['type']);
|
||||
array_push($records, $record);
|
||||
|
||||
$patch = array(
|
||||
'rrsets' => array(array(
|
||||
'name' => $record['name'],
|
||||
'type' => $record['type'],
|
||||
'changetype' => 'REPLACE',
|
||||
'records' => $records)));
|
||||
|
||||
api_request($zone['url'], $patch, 'PATCH');
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
function get_records_by_name_type($zone, $name, $type) {
|
||||
$zone = api_request($zone['url']);
|
||||
$records = array();
|
||||
foreach ($zone['records'] as $record) {
|
||||
if ($record['name'] == $name and
|
||||
$record['type'] == $type) {
|
||||
if ($record['name'] == $name and $record['type'] == $type) {
|
||||
array_push($records, $record);
|
||||
}
|
||||
}
|
||||
|
@ -107,74 +212,157 @@ function getrecords_by_name_type($zoneurl, $name, $type) {
|
|||
return $records;
|
||||
}
|
||||
|
||||
function zonesort($a, $b) {
|
||||
return strnatcmp($a["name"], $b["name"]);
|
||||
function decode_record_id($id) {
|
||||
$record = json_decode($id, 1);
|
||||
if (!$record
|
||||
|| !isset($record['name'])
|
||||
|| !isset($record['type'])
|
||||
|| !isset($record['ttl'])
|
||||
|| !isset($record['priority'])
|
||||
|| !isset($record['content'])
|
||||
|| !isset($record['disabled'])) {
|
||||
jtable_respond(null, 'error', "Invalid record id");
|
||||
}
|
||||
return $record;
|
||||
}
|
||||
|
||||
function add_db_zone($zone, $owner) {
|
||||
if (valid_user($owner) === FALSE) {
|
||||
jtable_respond(null, 'error', "$owner is not a valid username");
|
||||
}
|
||||
if (_valid_label($zone) === FALSE) {
|
||||
jtable_respond(null, 'error', "$zone is not a valid zonename");
|
||||
# get all records with same name and type but different id (content)
|
||||
# requires records with id to be present
|
||||
# SOA records match always, regardless of content.
|
||||
function get_records_except($zone, $exclude) {
|
||||
$is_soa = ($exclude['type'] == 'SOA');
|
||||
|
||||
$found = false;
|
||||
$zone = api_request($zone['url']);
|
||||
$records = array();
|
||||
foreach ($zone['records'] as $record) {
|
||||
if ($record['name'] == $exclude['name'] and $record['type'] == $exclude['type']) {
|
||||
if ($is_soa) {
|
||||
# SOA changes all the time (serial); we can't match it in a sane way.
|
||||
# OTOH we know it is unique anyway - just pretend we found a match.
|
||||
$found = true;
|
||||
} elseif ($record['content'] != $exclude['content']
|
||||
or $record['ttl'] != $exclude['ttl']
|
||||
or $record['priority'] != $exclude['priority']
|
||||
or $record['disabled'] != $exclude['disabled']) {
|
||||
array_push($records, $record);
|
||||
} else {
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_apiuser()) {
|
||||
if (!get_user_info($owner)) {
|
||||
add_user($owner);
|
||||
if (!$found) {
|
||||
header("Status: 404 Not Found");
|
||||
jtable_respond(null, 'error', "Didn't find record with id");
|
||||
}
|
||||
|
||||
return $records;
|
||||
}
|
||||
|
||||
function compareName($a, $b) {
|
||||
$a = array_reverse(explode('.', $a));
|
||||
$b = array_reverse(explode('.', $b));
|
||||
for ($i = 0; ; ++$i) {
|
||||
if (!isset($a[$i])) {
|
||||
return isset($b[$i]) ? -1 : 0;
|
||||
} else if (!isset($b[$i])) {
|
||||
return 1;
|
||||
}
|
||||
$cmp = strnatcasecmp($a[$i], $b[$i]);
|
||||
if ($cmp) {
|
||||
return $cmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function zone_compare($a, $b) {
|
||||
if ($cmp = compareName($a['name'], $b['name'])) return $cmp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function rrtype_compare($a, $b) {
|
||||
# sort specials before everything else
|
||||
$specials = array('SOA', 'NS', 'MX');
|
||||
$spa = array_search($a, $specials, true);
|
||||
$spb = array_search($b, $specials, true);
|
||||
if ($spa === false) {
|
||||
return ($spb === false) ? strcmp($a, $b) : 1;
|
||||
} else {
|
||||
return ($spb === false) ? -1 : $spa - $spb;
|
||||
}
|
||||
}
|
||||
|
||||
function record_compare($a, $b) {
|
||||
if ($cmp = compareName($a['name'], $b['name'])) return $cmp;
|
||||
if ($cmp = rrtype_compare($a['type'], $b['type'])) return $cmp;
|
||||
if ($cmp = ($a['priority'] - $b['priority'])) return $cmp;
|
||||
if ($cmp = strnatcasecmp($a['content'], $b['content'])) return $cmp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function add_db_zone($zonename, $ownername) {
|
||||
if (valid_user($ownername) === false) {
|
||||
jtable_respond(null, 'error', "$ownername is not a valid username");
|
||||
}
|
||||
if (!_valid_label($zonename)) {
|
||||
jtable_respond(null, 'error', "$zonename is not a valid zonename");
|
||||
}
|
||||
|
||||
if (is_apiuser() && !user_exists($ownername)) {
|
||||
add_user($ownername);
|
||||
}
|
||||
|
||||
$db = get_db();
|
||||
$q = $db->prepare("INSERT OR REPLACE INTO zones (zone, owner) VALUES (?, (SELECT id FROM users WHERE emailaddress = ?))");
|
||||
$q->bindValue(1, $zone, SQLITE3_TEXT);
|
||||
$q->bindValue(2, $owner, SQLITE3_TEXT);
|
||||
$q->bindValue(1, $zonename, SQLITE3_TEXT);
|
||||
$q->bindValue(2, $ownername, SQLITE3_TEXT);
|
||||
$q->execute();
|
||||
$db->close();
|
||||
}
|
||||
|
||||
function delete_db_zone($owner) {
|
||||
if (_valid_label($zone) === FALSE) {
|
||||
jtable_respond(null, 'error', "$zone is not a valid zonename");
|
||||
function delete_db_zone($zonename) {
|
||||
if (!_valid_label($zonename)) {
|
||||
jtable_respond(null, 'error', "$zonename is not a valid zonename");
|
||||
}
|
||||
$db = get_db();
|
||||
$q = $db->prepare("DELETE FROM zones WHERE zone = ?");
|
||||
$q->bindValue(1, $zone, SQLITE3_TEXT);
|
||||
$q->bindValue(1, $zonename, SQLITE3_TEXT);
|
||||
$q->execute();
|
||||
$db->close();
|
||||
}
|
||||
|
||||
function get_zone_owner($zone) {
|
||||
if (_valid_label($zone) === FALSE) {
|
||||
jtable_respond(null, 'error', "$zone is not a valid zonename");
|
||||
function get_zone_owner($zonename, $default) {
|
||||
if (!_valid_label($zonename)) {
|
||||
jtable_respond(null, 'error', "$zonename is not a valid zonename");
|
||||
}
|
||||
$db = get_db();
|
||||
$q = $db->prepare("SELECT u.emailaddress FROM users u, zones z WHERE z.owner = u.id AND z.zone = ?");
|
||||
$q->bindValue(1, $zone, SQLITE3_TEXT);
|
||||
$q->bindValue(1, $zonename, SQLITE3_TEXT);
|
||||
$result = $q->execute();
|
||||
$zoneinfo = $result->fetchArray(SQLITE3_ASSOC);
|
||||
$db->close();
|
||||
if (isset($zoneinfo['emailaddress']) && $zoneinfo['emailaddress'] != NULL ) {
|
||||
if (isset($zoneinfo['emailaddress']) && $zoneinfo['emailaddress'] != null ) {
|
||||
return $zoneinfo['emailaddress'];
|
||||
}
|
||||
|
||||
return 'admin';
|
||||
return $default;
|
||||
}
|
||||
|
||||
function get_zone_keys($zone) {
|
||||
$ret = array();
|
||||
foreach (json_decode(_do_curl("/servers/:serverid:/zones/".$zone."/cryptokeys"), 1) as $key) {
|
||||
foreach (api_request($zone['url'] . "/cryptokeys") as $key) {
|
||||
if (!isset($key['active']))
|
||||
continue;
|
||||
|
||||
$key['dstxt'] = $zone.' IN DNSKEY '.$key['dnskey']."\n\n";
|
||||
$key['dstxt'] = $zone['name'] . ' IN DNSKEY '.$key['dnskey']."\n\n";
|
||||
|
||||
if (isset($key['ds'])) {
|
||||
foreach ($key['ds'] as $ds) {
|
||||
$key['dstxt'] .= $zone.' IN DS '.$ds."\n";
|
||||
$key['dstxt'] .= $zone['name'] . ' IN DS '.$ds."\n";
|
||||
}
|
||||
unset($key['ds']);
|
||||
}
|
||||
unset($key['ds']);
|
||||
$ret[] = $key;
|
||||
}
|
||||
|
||||
|
@ -182,15 +370,7 @@ function get_zone_keys($zone) {
|
|||
}
|
||||
|
||||
function check_owner($zone) {
|
||||
if (is_adminuser() === TRUE) {
|
||||
return TRUE ;
|
||||
}
|
||||
|
||||
if (get_zone_owner($zone) == get_sess_user()) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
return is_adminuser() or ($zone['owner'] === get_sess_user());
|
||||
}
|
||||
|
||||
if (isset($_GET['action'])) {
|
||||
|
@ -199,185 +379,229 @@ if (isset($_GET['action'])) {
|
|||
jtable_respond(null, 'error', 'No action given');
|
||||
}
|
||||
|
||||
if ($action == "list" or $action== "listslaves") {
|
||||
$rows = json_decode(_do_curl('servers/:serverid:/zones'), 1);
|
||||
switch ($action) {
|
||||
|
||||
case "list":
|
||||
case "listslaves":
|
||||
$return = array();
|
||||
foreach ($rows as $zone) {
|
||||
if (check_owner($zone['name']) === FALSE)
|
||||
$q = isset($_POST['domsearch']) ? $_POST['domsearch'] : false;
|
||||
foreach (get_all_zones() as $zone) {
|
||||
$zone['owner'] = get_zone_owner($zone['name'], 'admin');
|
||||
if (!check_owner($zone))
|
||||
continue;
|
||||
|
||||
if (isset($_POST['domsearch'])) {
|
||||
$q = $_POST['domsearch'];
|
||||
if (!preg_match("/$q/", $zone['name']) == 1) {
|
||||
continue;
|
||||
}
|
||||
if ($q && !preg_match("/$q/", $zone['name'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$zone['name'] = htmlspecialchars($zone['name']);
|
||||
$zone['owner'] = get_zone_owner($zone['name']);
|
||||
if ($action == "listslaves" and $zone['kind'] == "Slave") {
|
||||
array_push($return, $zone);
|
||||
} elseif ($action == "list" and $zone['kind'] != "Slave") {
|
||||
if ($zone['dnssec'] == true) {
|
||||
$zone['keyinfo'] = get_zone_keys($zone['name']);
|
||||
if ($zone['dnssec']) {
|
||||
$zone['keyinfo'] = get_zone_keys($zone);
|
||||
}
|
||||
array_push($return, $zone);
|
||||
}
|
||||
}
|
||||
usort($return, "zonesort");
|
||||
usort($return, "zone_compare");
|
||||
jtable_respond($return);
|
||||
} elseif ($action == "create") {
|
||||
if (is_adminuser() !== TRUE and $allowzoneadd !== TRUE) {
|
||||
break;
|
||||
|
||||
case "create":
|
||||
$zonename = isset($_POST['name']) ? $_POST['name'] : '';
|
||||
$zonekind = isset($_POST['kind']) ? $_POST['kind'] : '';
|
||||
|
||||
if (!is_adminuser() and $allowzoneadd !== true) {
|
||||
jtable_respond(null, 'error', "You are not allowed to add zones");
|
||||
}
|
||||
if (_valid_label($_POST['name']) === FALSE) {
|
||||
if (!_valid_label($zonename)) {
|
||||
jtable_respond(null, 'error', "Please only use [a-z0-9_/.-]");
|
||||
}
|
||||
if (is_ascii($_POST['name']) === FALSE) {
|
||||
jtable_respond(null, 'error', "Please only use ASCII-characters in your domainname");
|
||||
}
|
||||
if ($_POST['kind'] != null and $_POST['name'] != null) {
|
||||
$nameservers = array();
|
||||
if ($_POST['kind'] != "Slave") {
|
||||
if (isset($_POST['nameserver1']) && $_POST['nameserver1'] != null) {
|
||||
array_push($nameservers, $_POST['nameserver1']);
|
||||
if (isset($_POST['nameserver2']) && $_POST['nameserver2'] != null) {
|
||||
array_push($nameservers, $_POST['nameserver2']);
|
||||
}
|
||||
} else {
|
||||
jtable_respond(null, 'error', "Not enough data: ".print_r($_POST, 1));
|
||||
}
|
||||
if (isset($defaults['soa_edit_api'])) {
|
||||
$vars['soa_edit_api'] = $defaults['soa_edit_api'];
|
||||
}
|
||||
}
|
||||
$vars['name'] = $_POST['name'];
|
||||
$vars['kind'] = $_POST['kind'];
|
||||
if (isset($_POST['zone'])) {
|
||||
$vars['zone'] = $_POST['zone'];
|
||||
$vars['nameservers'] = array();
|
||||
} else {
|
||||
$vars['nameservers'] = $nameservers;
|
||||
}
|
||||
_do_curl('/servers/:serverid:/zones', $vars);
|
||||
if (isset($_POST['owner']) and $_POST['owner'] != 'admin') {
|
||||
add_db_zone($vars['name'], $_POST['owner']);
|
||||
} else {
|
||||
add_db_zone($vars['name'], get_sess_user());
|
||||
}
|
||||
if (isset($_POST['template']) && $_POST['template'] != 'None') {
|
||||
foreach ($templates as $template) {
|
||||
if ($template['name'] == $_POST['template']) {
|
||||
$zoneurl = '/servers/:serverid:/zones/'.$vars['name'].'.';
|
||||
foreach ($template['records'] as $record) {
|
||||
if ($record['label'] != "") {
|
||||
$name = join('.', array($record['label'], $vars['name']));
|
||||
} else {
|
||||
$name = $vars['name'];
|
||||
}
|
||||
if (!isset($record['name'])) {
|
||||
$record['name'] = "";
|
||||
}
|
||||
$records = getrecords_by_name_type($zoneurl, $name, $record['type']);
|
||||
$records = _create_record($name, $records, $record, $zoneurl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$zonename || !$zonekind) {
|
||||
jtable_respond(null, 'error', "Not enough data");
|
||||
}
|
||||
|
||||
$createOptions = array(
|
||||
'name' => $zonename,
|
||||
'kind' => $zonekind,
|
||||
);
|
||||
|
||||
$nameservers = array();
|
||||
if (isset($_POST['nameserver1']) && $_POST['nameserver1'] != null) {
|
||||
array_push($nameservers, $_POST['nameserver1']);
|
||||
}
|
||||
if (isset($_POST['nameserver2']) && $_POST['nameserver2'] != null) {
|
||||
array_push($nameservers, $_POST['nameserver2']);
|
||||
}
|
||||
|
||||
if ($zonekind != "Slave") {
|
||||
if (!isset($_POST['zone'])) {
|
||||
$vars = $_POST;
|
||||
$vars['serial'] = 0;
|
||||
$vars['records'] = array();
|
||||
jtable_respond($vars, 'single');
|
||||
}
|
||||
$zoneurl = '/servers/:serverid:/zones/'.$vars['name'].'.';
|
||||
if (isset($_POST['owns'])) {
|
||||
$patch = array();
|
||||
$patch['rrsets'] = array();
|
||||
array_push($patch['rrsets'], array(
|
||||
'comments' => array(),
|
||||
'records' => array(),
|
||||
'changetype'=> "REPLACE",
|
||||
'type' => 'NS',
|
||||
'name' => $vars['name']));
|
||||
_do_curl($zoneurl, $patch, 'patch');
|
||||
foreach ($nameservers as $ns) {
|
||||
$records = getrecords_by_name_type($zoneurl, $vars['name'], 'NS');
|
||||
$records = _create_record($vars['name'], $records, array('type' => 'NS', 'name' => '', 'content' => $ns), $zoneurl);
|
||||
if (0 == count($nameservers)) {
|
||||
jtable_respond(null, 'error', "Require nameservers");
|
||||
}
|
||||
}
|
||||
|
||||
$vars = _do_curl($zoneurl);
|
||||
jtable_respond($vars, 'single');
|
||||
} else {
|
||||
jtable_respond(null, 'error', "Not enough data: ".print_r($_POST, 1));
|
||||
}
|
||||
} elseif ($action == "listrecords" && $_GET['zoneurl'] != null) {
|
||||
$rows = json_decode(_do_curl($_GET['zoneurl']), 1);
|
||||
$soa = array();
|
||||
$ns = array();
|
||||
$mx = array();
|
||||
$any = array();
|
||||
foreach ($rows['records'] as $idx => $record) {
|
||||
$rows['records'][$idx]['id'] = json_encode($record);
|
||||
$rows['records'][$idx]['name'] = htmlspecialchars($record['name']);
|
||||
if ($record['type'] == 'SOA') { array_push($soa, $rows['records'][$idx]); }
|
||||
elseif ($record['type'] == 'NS') { array_push($ns, $rows['records'][$idx]); }
|
||||
elseif ($record['type'] == 'MX') { array_push($mx, $rows['records'][$idx]); }
|
||||
else {
|
||||
array_push($any, $rows['records'][$idx]);
|
||||
};
|
||||
}
|
||||
usort($any, "zonesort");
|
||||
$ret = array_merge($soa, $ns, $mx, $any);
|
||||
jtable_respond($ret);
|
||||
} elseif ($action == "delete") {
|
||||
_do_curl("/servers/:serverid:/zones/".$_POST['id'], array(), 'delete');
|
||||
$zone = preg_replace("/\.$/", "", $_POST['id']);
|
||||
delete_db_zone($zone);
|
||||
jtable_respond(null, 'delete');
|
||||
} elseif ($action == "createrecord" or $action == "editrecord") {
|
||||
$name = (!preg_match("/\.".$_POST['domain']."\.?$/", $_POST['name'])) ? $_POST['name'].'.'.$_POST['domain'] : $_POST['name'];
|
||||
$name = preg_replace("/\.$/", "", $name);
|
||||
$name = preg_replace("/^\./", "", $name);
|
||||
$records = array();
|
||||
if ($action == "createrecord") {
|
||||
$records = getrecords_by_name_type($_GET['zoneurl'], $name, $_POST['type']);
|
||||
}
|
||||
|
||||
$records =_create_record($name, $records, $_POST, $_GET['zoneurl']);
|
||||
jtable_respond($records[sizeof($records)-1], 'single');
|
||||
} elseif ($action == "deleterecord") {
|
||||
$todel = json_decode($_POST['id'], 1);
|
||||
$records = getrecords_by_name_type($_GET['zoneurl'], $todel['name'], $todel['type']);
|
||||
$precords = array();
|
||||
|
||||
foreach ($records as $record) {
|
||||
if (
|
||||
$record['content'] == $todel['content'] and
|
||||
$record['type'] == $todel['type'] and
|
||||
$record['prio'] == $todel['prio'] and
|
||||
$record['name'] == $todel['name']) {
|
||||
continue;
|
||||
$createOptions['nameservers'] = $nameservers;
|
||||
} else {
|
||||
array_push($precords, $record);
|
||||
$createOptions['zone'] = $_POST['zone'];
|
||||
}
|
||||
if (isset($defaults['soa_edit_api'])) {
|
||||
$createOptions['soa_edit_api'] = $defaults['soa_edit_api'];
|
||||
}
|
||||
if (isset($defaults['soa_edit'])) {
|
||||
$createOptions['soa_edit'] = $defaults['soa_edit'];
|
||||
}
|
||||
} else { // Slave
|
||||
if (isset($_POST['masters'])) {
|
||||
$createOptions['masters'] = preg_split('/[,;\s]+/', $_POST['masters'], null, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
if (0 == count($createOptions['masters'])) {
|
||||
jtable_respond(null, 'error', "Slave requires master servers");
|
||||
}
|
||||
}
|
||||
$patch = array();
|
||||
$patch['rrsets'] = array();
|
||||
array_push($patch['rrsets'], array(
|
||||
'comments' => array(),
|
||||
'records' => $precords,
|
||||
'changetype'=> "REPLACE",
|
||||
'type' => $todel['type'],
|
||||
'name' => $todel['name']));
|
||||
_do_curl($_GET['zoneurl'], $patch, 'patch');
|
||||
|
||||
// only admin user and original owner can "recreate" zones that are already
|
||||
// present in our own db but got lost in pdns.
|
||||
if (!is_adminuser() && get_sess_user() !== get_zone_owner($zonename, get_sess_user())) {
|
||||
jtable_respond(null, 'error', 'Zone already owned by someone else');
|
||||
}
|
||||
|
||||
$zone = zones_api_request($createOptions);
|
||||
$zonename = $zone['name'];
|
||||
|
||||
if (is_adminuser() && isset($_POST['owner'])) {
|
||||
add_db_zone($zonename, $_POST['owner']);
|
||||
} else {
|
||||
add_db_zone($zonename, get_sess_user());
|
||||
}
|
||||
|
||||
if (isset($_POST['template']) && $_POST['template'] != 'None') {
|
||||
foreach (user_template_list() as $template) {
|
||||
if ($template['name'] !== $_POST['template']) continue;
|
||||
|
||||
foreach ($template['records'] as $record) {
|
||||
if (isset($record['label'])) {
|
||||
$record['name'] = $record['label'];
|
||||
unset($record['label']);
|
||||
}
|
||||
create_record($zone, $record);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['zone']) && isset($_POST['owns']) && $_POST['owns'] && count($nameservers)) {
|
||||
$records = array();
|
||||
foreach ($nameservers as $ns) {
|
||||
array_push($records, array('type' => 'NS', 'content' => $ns));
|
||||
}
|
||||
update_records($zone, $records[0], $records);
|
||||
}
|
||||
|
||||
unset($zone['records']);
|
||||
unset($zone['comments']);
|
||||
jtable_respond($zone, 'single');
|
||||
break;
|
||||
|
||||
case "update":
|
||||
$zone = get_zone_by_id(isset($_POST['id']) ? $_POST['id'] : '');
|
||||
|
||||
$zoneowner = isset($_POST['owner']) ? $_POST['owner'] : $zone['owner'];
|
||||
|
||||
if ($zone['owner'] !== $zoneowner) {
|
||||
if (!is_adminuser()) {
|
||||
header("Status: 403 Access denied");
|
||||
jtable_respond(null, 'error', "Can't change owner");
|
||||
} else {
|
||||
add_db_zone($zone['name'], $zoneowner);
|
||||
$zone['owner'] = $zoneowner;
|
||||
}
|
||||
}
|
||||
|
||||
$update = false;
|
||||
|
||||
if (isset($_POST['masters'])) {
|
||||
$zone['masters'] = preg_split('/[,;\s]+/', $_POST['masters'], null, PREG_SPLIT_NO_EMPTY);
|
||||
$update = true;
|
||||
}
|
||||
|
||||
if ($update) {
|
||||
$zoneUpdate = $zone;
|
||||
unset($zoneUpdate['id']);
|
||||
unset($zoneUpdate['url']);
|
||||
unset($zoneUpdate['owner']);
|
||||
$newZone = api_request($zone['url'], $zoneUpdate, 'PUT');
|
||||
$newZone['owner'] = $zone['owner'];
|
||||
} else {
|
||||
$newZone = $zone;
|
||||
}
|
||||
unset($newZone['records']);
|
||||
unset($newZone['comments']);
|
||||
|
||||
jtable_respond($newZone, 'single');
|
||||
break;
|
||||
|
||||
case "delete":
|
||||
$zone = get_zone_by_id(isset($_POST['id']) ? $_POST['id'] : '');
|
||||
|
||||
api_request($zone['url'], array(), 'DELETE');
|
||||
delete_db_zone($zone['name']);
|
||||
jtable_respond(null, 'delete');
|
||||
} elseif ($action == "update") {
|
||||
add_db_zone($_POST['name'], $_POST['owner']);
|
||||
jtable_respond($_POST, 'single');
|
||||
} else {
|
||||
break;
|
||||
|
||||
case "listrecords":
|
||||
$zone = get_zone_by_url(isset($_GET['zoneurl']) ? $_GET['zoneurl'] : '');
|
||||
|
||||
$records = api_request($zone['url'])['records'];
|
||||
foreach ($records as &$record) {
|
||||
$record['id'] = json_encode($record);
|
||||
}
|
||||
unset($record);
|
||||
usort($records, "record_compare");
|
||||
jtable_respond($records);
|
||||
break;
|
||||
|
||||
case "createrecord":
|
||||
$zone = get_zone_by_url(isset($_GET['zoneurl']) ? $_GET['zoneurl'] : '');
|
||||
$record = create_record($zone, $_POST);
|
||||
|
||||
$record['id'] = json_encode($record);
|
||||
jtable_respond($record, 'single');
|
||||
break;
|
||||
|
||||
case "editrecord":
|
||||
$zone = get_zone_by_url(isset($_GET['zoneurl']) ? $_GET['zoneurl'] : '');
|
||||
$old_record = decode_record_id(isset($_POST['id']) ? $_POST['id'] : '');
|
||||
|
||||
$records = get_records_except($zone, $old_record);
|
||||
|
||||
$record = make_record($zone, $_POST);
|
||||
|
||||
if ($record['name'] !== $old_record['name']) {
|
||||
# rename:
|
||||
$newRecords = get_records_by_name_type($zone, $record['name'], $record['type']);
|
||||
array_push($newRecords, $record);
|
||||
update_records($zone, $old_record, $records); # remove from old list
|
||||
update_records($zone, $record, $newRecords); # add to new list
|
||||
} else {
|
||||
array_push($records, $record);
|
||||
update_records($zone, $record, $records);
|
||||
}
|
||||
|
||||
$record['id'] = json_encode($record);
|
||||
jtable_respond($record, 'single');
|
||||
break;
|
||||
|
||||
case "deleterecord":
|
||||
$zone = get_zone_by_url(isset($_GET['zoneurl']) ? $_GET['zoneurl'] : '');
|
||||
$old_record = decode_record_id(isset($_POST['id']) ? $_POST['id'] : '');
|
||||
|
||||
$records = get_records_except($zone, $old_record);
|
||||
|
||||
update_records($zone, $old_record, $records);
|
||||
jtable_respond(null, 'delete');
|
||||
break;
|
||||
|
||||
default:
|
||||
jtable_respond(null, 'error', 'No such action');
|
||||
break;
|
||||
}
|
||||
?>
|
||||
|
|
Loading…
Add table
Reference in a new issue