mirror of
https://github.com/tuxis-ie/nsedit.git
synced 2025-04-19 20:09:14 +03:00
Compare commits
No commits in common. "master" and "v0.9" have entirely different histories.
19 changed files with 683 additions and 1917 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,4 +1 @@
|
|||
includes/config.inc.php
|
||||
nsedit.sublime*
|
||||
etc
|
||||
templates.d/*.json
|
||||
|
|
32
Dockerfile
32
Dockerfile
|
@ -1,28 +1,38 @@
|
|||
FROM debian:bookworm
|
||||
FROM debian:jessie
|
||||
MAINTAINER Yury Evtikhov <yury@evtikhov.info>
|
||||
#
|
||||
# This Dockerfile is intended only for test/development use.
|
||||
# It will be a really BAD idea to use it for production or public services.
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
#
|
||||
# Please set the following variables before building:
|
||||
#
|
||||
ENV PDNSAPIPWD mypowerdnsapipassword
|
||||
ENV PDNSAPIIP 192.168.1.2
|
||||
ENV PDNSAPIPORT 8081
|
||||
|
||||
# Update and Upgrade system
|
||||
RUN apt-get -y update && \
|
||||
apt-get -y install curl git-core php8.2-cli php8.2-curl php8.2-sqlite3 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
RUN mkdir /app
|
||||
RUN git clone --recursive https://github.com/tuxis-ie/nsedit.git /app/nsedit
|
||||
RUN cp /app/nsedit/includes/config.inc.php-dist /app/nsedit/includes/config.inc.php
|
||||
COPY docker-entrypoint.sh /app/nsedit/docker-entrypoint.sh
|
||||
RUN chmod +x /app/nsedit/docker-entrypoint.sh
|
||||
|
||||
apt-get -y install curl git-core php5-cli php5-curl php5-json php5-sqlite && \
|
||||
mkdir /app && \
|
||||
git clone --recursive https://github.com/tuxis-ie/nsedit.git /app/nsedit && \
|
||||
cp /app/nsedit/includes/config.inc.php-dist /app/nsedit/includes/config.inc.php && \
|
||||
sed "s/\$apipass = ''/\$apipass = '$PDNSAPIPWD'/" -i /app/nsedit/includes/config.inc.php && \
|
||||
sed "s/\$apiip = ''/\$apiip = '$PDNSAPIIP'/" -i /app/nsedit/includes/config.inc.php && \
|
||||
sed "s/\$apiport = ''/\$apiport = '$PDNSAPIPORT'/" -i /app/nsedit/includes/config.inc.php && \
|
||||
sed "s/\$authdb = \"\.\.\/etc\/pdns\.users\.sqlite3\"/\$authdb = \"\/app\/pdns\.users\.sqlite3\"/" -i /app/nsedit/includes/config.inc.php
|
||||
|
||||
# Define working directory.
|
||||
VOLUME /app/nsedit
|
||||
WORKDIR /app/nsedit
|
||||
EXPOSE 8080
|
||||
|
||||
CMD ["sh", "-c", "/app/nsedit/docker-entrypoint.sh"]
|
||||
ENTRYPOINT ["/usr/bin/php", "-S", "0.0.0.0:8080"]
|
||||
|
||||
#
|
||||
# Usage:
|
||||
|
|
37
README.md
37
README.md
|
@ -10,16 +10,14 @@ Features
|
|||
========
|
||||
* Import BIND- or AXFR-style dumps of your existing zones
|
||||
* Add/remove zones and records
|
||||
* Clone zones
|
||||
* Show the DNSsec details of a zone
|
||||
* Multiple user support
|
||||
* Allow logging of all actions in NSEdit, including exporting the log in JSON-format
|
||||
* Multiple user support
|
||||
* [experimental] nsedit API, to create zones from another system
|
||||
|
||||
User support
|
||||
============
|
||||
Multiple users are supported. A user can be an admin or a normal user. You can
|
||||
configure whether or not a normal user is allowed to add new zones.
|
||||
configure wheter or not a normal user is allowed to add new zones.
|
||||
|
||||
WeFact Login support
|
||||
====================
|
||||
|
@ -34,7 +32,7 @@ Requirements
|
|||
* php sqlite3
|
||||
* php curl
|
||||
* php with openssl support
|
||||
* PowerDNS with the JSON-api enabled. Version 4.0.0 or greater
|
||||
* PowerDNS with the experimental JSON-api enabled (3.4.0 should do. For Pdns > 4.0.0 you ***NEED*** v1.0 of NSEdit)
|
||||
|
||||
Installing
|
||||
==========
|
||||
|
@ -43,42 +41,19 @@ Installing
|
|||
- Run git clone in the directory where you want to run nsedit from
|
||||
: ```git clone https://github.com/tuxis-ie/nsedit.git```
|
||||
|
||||
- Select tag v1.0 or skip this if you want to run from master
|
||||
: ```git checkout tags/v1.0```
|
||||
- Select tag v0.9 or skip this if you want to run from master
|
||||
: ```git checkout tags/v0.9```
|
||||
* Via releases
|
||||
- Download the zip-file from [Releases](https://github.com/tuxis-ie/nsedit/releases)
|
||||
|
||||
* Copy ```includes/config.inc.php-dist``` to ```includes/config.inc.php``` and edit config.inc.php to your needs.
|
||||
|
||||
* By default, nsedit writes its user database to ../etc/pdns.users.sqlite3. Be sure that your webserver can create that directory and write to it. **Make sure the Webserver doesn't serve this file/folder to the public!**
|
||||
* By default, nsedit writes its user database to ../etc/pdns.users.sqlite3. Be sure that your webserver can create that directory and write to it.
|
||||
|
||||
* Visit http(s)://<url>/nsedit/ and login with admin/admin (Don't forget to update your password!)
|
||||
|
||||
Have fun ;)
|
||||
|
||||
Other methods of installation (Unsupported)
|
||||
===========================================
|
||||
|
||||
* Baji Zsolt created a Suse image: https://susestudio.com/a/vvnMqa/powerdns-with-nsedit
|
||||
* Yury Evtikhov created the Docker file: https://github.com/tuxis-ie/nsedit/blob/master/Dockerfile
|
||||
|
||||
Configuring PowerDNS
|
||||
====================
|
||||
Minimal configuration of PowerDNS for supporting nsedit has to include 3 directives:
|
||||
```
|
||||
webserver=yes
|
||||
api=yes
|
||||
api-key=SomeRandomString
|
||||
```
|
||||
|
||||
Special note for Ubuntu Xenial Xerus 16.04 users:
|
||||
Default `pdns` package included in Ubuntu repositories has the version of 4.0.0-alpha2 and *nsedit v1.0* doesn't work with it due to API incompatibility.
|
||||
|
||||
If your PowerDNS version is not the latest one, please consider adding PowerDNS repository to your system.
|
||||
|
||||
Detailed instructions for adding repository are available at http://repo.powerdns.com/
|
||||
|
||||
|
||||
Screenshots
|
||||
===========
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ body, html {
|
|||
border: 1px solid #DDD;
|
||||
font-family: 'Segoe UI Semilight','Open Sans',Verdana,Arial,Helvetica,sans-serif;
|
||||
font-weight: 300;
|
||||
float: left;
|
||||
font-size: 14px;
|
||||
line-height: 1.3;
|
||||
width: 10%;
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
[ -z "$PDNSAPIIP" ] && echo "Set PDNSAPIIP to your PowerDNS API IP/Hostname" && exit 1;
|
||||
[ -z "$PDNSAPIPWD" ] && echo "Set PDNSAPIPWD to your PowerDNS API Password" && exit 1;
|
||||
|
||||
sed "s/\$apipass = ''/\$apipass = '$PDNSAPIPWD'/" -i /app/nsedit/includes/config.inc.php
|
||||
sed "s/\$apiip = ''/\$apiip = '$PDNSAPIIP'/" -i /app/nsedit/includes/config.inc.php
|
||||
if [[ $PDNSAPIPORT && ${PDNSAPIPORT-x} ]]
|
||||
then
|
||||
sed "s/\$apiport = '8081'/\$apiport = '$PDNSAPIPORT'/" -i /app/nsedit/includes/config.inc.php
|
||||
fi
|
||||
sed "s/\$authdb = \"\.\.\/etc\/pdns\.users\.sqlite3\"/\$authdb = \"\/app\/pdns\.users\.sqlite3\"/" -i /app/nsedit/includes/config.inc.php
|
||||
|
||||
exec /usr/bin/php -S 0.0.0.0:8080
|
BIN
img/delete.png
BIN
img/delete.png
Binary file not shown.
Before Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 278 B |
|
@ -1,135 +0,0 @@
|
|||
<?php
|
||||
|
||||
include_once('includes/config.inc.php');
|
||||
|
||||
class ApiHandler {
|
||||
public $headers;
|
||||
public $hostname;
|
||||
public $port;
|
||||
public $auth;
|
||||
public $proto;
|
||||
public $sslverify;
|
||||
public $curlh;
|
||||
public $method;
|
||||
public $content;
|
||||
public $apiurl;
|
||||
public $url;
|
||||
public $json;
|
||||
|
||||
public function __construct() {
|
||||
global $apiip, $apiport, $apipass, $apiproto, $apisslverify;
|
||||
|
||||
$this->headers = Array();
|
||||
$this->hostname = $apiip;
|
||||
$this->port = $apiport;
|
||||
$this->auth = $apipass;
|
||||
$this->proto = $apiproto;
|
||||
$this->sslverify = $apisslverify;
|
||||
$this->curlh = curl_init();
|
||||
$this->method = 'GET';
|
||||
$this->content = FALSE;
|
||||
$this->apiurl = '';
|
||||
}
|
||||
|
||||
public function addheader($field, $content) {
|
||||
$this->headers[$field] = $content;
|
||||
}
|
||||
|
||||
private function authheaders() {
|
||||
$this->addheader('X-API-Key', $this->auth);
|
||||
}
|
||||
|
||||
private function apiurl() {
|
||||
$tmp = new ApiHandler();
|
||||
|
||||
$tmp->url = '/api/v1/servers/localhost';
|
||||
$tmp->go();
|
||||
|
||||
$this->apiurl = $tmp->json["url"];
|
||||
}
|
||||
|
||||
private function curlopts() {
|
||||
$this->authheaders();
|
||||
$this->addheader('Accept', 'application/json');
|
||||
|
||||
if(defined('curl_reset')) {
|
||||
curl_reset($this->curlh);
|
||||
} else {
|
||||
$this->curlh = curl_init();
|
||||
}
|
||||
curl_setopt($this->curlh, CURLOPT_HTTPHEADER, Array());
|
||||
curl_setopt($this->curlh, CURLOPT_RETURNTRANSFER, 1);
|
||||
|
||||
if (strcasecmp($this->proto, 'https')) {
|
||||
curl_setopt($this->curlh, CURLOPT_SSL_VERIFYPEER, $this->sslverify);
|
||||
}
|
||||
|
||||
$setheaders = Array();
|
||||
|
||||
foreach ($this->headers as $k => $v) {
|
||||
array_push($setheaders, join(": ", Array($k, $v)));
|
||||
}
|
||||
curl_setopt($this->curlh, CURLOPT_HTTPHEADER, $setheaders);
|
||||
}
|
||||
|
||||
private function baseurl() {
|
||||
$ip = $this->hostname;
|
||||
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
$ip = sprintf('[%s]', $ip); // curl needs brackets for IPv6
|
||||
}
|
||||
|
||||
return $this->proto.'://'.$ip.':'.$this->port.$this->apiurl;
|
||||
}
|
||||
|
||||
private function go() {
|
||||
$this->curlopts();
|
||||
|
||||
if ($this->content) {
|
||||
$this->addheader('Content-Type', 'application/json');
|
||||
curl_setopt($this->curlh, CURLOPT_POST, 1);
|
||||
curl_setopt($this->curlh, CURLOPT_POSTFIELDS, $this->content);
|
||||
}
|
||||
|
||||
switch ($this->method) {
|
||||
case 'POST':
|
||||
curl_setopt($this->curlh, CURLOPT_POST, 1);
|
||||
break;
|
||||
case 'GET':
|
||||
curl_setopt($this->curlh, CURLOPT_POST, 0);
|
||||
break;
|
||||
case 'DELETE':
|
||||
case 'PATCH':
|
||||
case 'PUT':
|
||||
curl_setopt($this->curlh, CURLOPT_CUSTOMREQUEST, $this->method);
|
||||
break;
|
||||
}
|
||||
|
||||
curl_setopt($this->curlh, CURLOPT_URL, $this->baseurl().$this->url);
|
||||
|
||||
$return = curl_exec($this->curlh);
|
||||
$code = curl_getinfo($this->curlh, CURLINFO_HTTP_CODE);
|
||||
$json = json_decode($return, 1);
|
||||
|
||||
if (isset($json['error'])) {
|
||||
throw new Exception("API Error $code: ".$json['error']);
|
||||
} elseif ($code < 200 || $code >= 300) {
|
||||
if ($code == 401) {
|
||||
throw new Exception("Authentication failed. Have you configured your authmethod correct?");
|
||||
}
|
||||
throw new Exception("Curl Error: $code ".curl_error($this->curlh));
|
||||
}
|
||||
|
||||
$this->json = $json;
|
||||
}
|
||||
|
||||
public function call() {
|
||||
if (substr($this->url, 0, 1) != '/') {
|
||||
$this->url = '/'.$this->url;
|
||||
}
|
||||
$this->apiurl();
|
||||
$this->url = str_replace($this->apiurl, '', $this->url);
|
||||
$this->go();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
<?php
|
||||
|
||||
include_once('ApiHandler.php');
|
||||
|
||||
class PdnsAPI {
|
||||
public $http;
|
||||
|
||||
|
||||
public function __construct() {
|
||||
$this->http = new ApiHandler();
|
||||
}
|
||||
|
||||
public function listzones($q = FALSE) {
|
||||
$api = clone $this->http;
|
||||
$api->method = 'GET';
|
||||
if ($q) {
|
||||
$api->url = "/search-data?q=*".$q."*&max=25";
|
||||
$api->call();
|
||||
$ret = Array();
|
||||
$seen = Array();
|
||||
|
||||
foreach ($api->json as $result) {
|
||||
if (isset($seen[$result['zone_id']])) {
|
||||
continue;
|
||||
}
|
||||
$zone = $this->loadzone($result['zone_id']);
|
||||
unset($zone['rrsets']);
|
||||
array_push($ret, $zone);
|
||||
$seen[$result['zone_id']] = 1;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
$api->url = "/zones";
|
||||
$api->call();
|
||||
|
||||
return $api->json;
|
||||
}
|
||||
|
||||
public function loadzone($zoneid) {
|
||||
$api = clone $this->http;
|
||||
$api->method = 'GET';
|
||||
$api->url = "/zones/$zoneid";
|
||||
$api->call();
|
||||
|
||||
return $api->json;
|
||||
}
|
||||
|
||||
public function exportzone($zoneid) {
|
||||
$api = clone $this->http;
|
||||
$api->method = 'GET';
|
||||
$api->url = "/zones/$zoneid/export";
|
||||
$api->call();
|
||||
|
||||
return $api->json;
|
||||
}
|
||||
|
||||
public function savezone($zone) {
|
||||
$api = clone $this->http;
|
||||
// We have to split up RRSets and Zoneinfo.
|
||||
// First, update the zone
|
||||
|
||||
$zonedata = $zone;
|
||||
unset($zonedata['id']);
|
||||
unset($zonedata['url']);
|
||||
unset($zonedata['rrsets']);
|
||||
|
||||
if (!isset($zone['serial']) or gettype($zone['serial']) != 'integer') {
|
||||
$api->method = 'POST';
|
||||
$api->url = '/zones';
|
||||
$api->content = json_encode($zonedata);
|
||||
$api->call();
|
||||
|
||||
return $api->json;
|
||||
}
|
||||
$api->method = 'PUT';
|
||||
$api->url = $zone['url'];
|
||||
$api->content = json_encode($zonedata);
|
||||
$api->call();
|
||||
|
||||
// Then, update the rrsets
|
||||
if (count($zone['rrsets']) > 0) {
|
||||
$api->method = 'PATCH';
|
||||
$api->content = json_encode(Array('rrsets' => $zone['rrsets']));
|
||||
$api->call();
|
||||
}
|
||||
|
||||
return $this->loadzone($zone['id']);
|
||||
}
|
||||
|
||||
public function deletezone($zoneid) {
|
||||
$api = clone $this->http;
|
||||
$api->method = 'DELETE';
|
||||
$api->url = "/zones/$zoneid";
|
||||
$api->call();
|
||||
|
||||
return $api->json;
|
||||
}
|
||||
|
||||
public function getzonekeys($zoneid) {
|
||||
$ret = array();
|
||||
$api = clone $this->http;
|
||||
$api->method = 'GET';
|
||||
$api->url = "/zones/$zoneid/cryptokeys";
|
||||
|
||||
$api->call();
|
||||
|
||||
foreach ($api->json as $key) {
|
||||
if (!isset($key['active']))
|
||||
continue;
|
||||
|
||||
$key['dstxt'] = $zoneid . ' IN DNSKEY '.$key['dnskey']."\n\n";
|
||||
|
||||
if (isset($key['ds'])) {
|
||||
foreach ($key['ds'] as $ds) {
|
||||
$key['dstxt'] .= $zoneid . ' IN DS '.$ds."\n";
|
||||
}
|
||||
unset($key['ds']);
|
||||
}
|
||||
array_push($ret, $key);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,376 +0,0 @@
|
|||
<?php
|
||||
|
||||
class Zone {
|
||||
public $id;
|
||||
public $name;
|
||||
public $kind;
|
||||
public $url;
|
||||
public $serial;
|
||||
public $dnssec;
|
||||
public $soa_edit;
|
||||
public $soa_edit_api;
|
||||
public $keyinfo;
|
||||
public $account;
|
||||
public $zone;
|
||||
public $nameservers;
|
||||
public $rrsets;
|
||||
public $masters;
|
||||
|
||||
public function __construct() {
|
||||
$this->id = '';
|
||||
$this->name = '';
|
||||
$this->kind = '';
|
||||
$this->url = '';
|
||||
$this->serial = '';
|
||||
$this->dnssec = '';
|
||||
$this->soa_edit = '';
|
||||
$this->soa_edit_api = '';
|
||||
$this->keyinfo = '';
|
||||
$this->account = '';
|
||||
$this->zone = FALSE;
|
||||
$this->nameservers = Array();
|
||||
$this->rrsets = Array();
|
||||
$this->masters = Array();
|
||||
}
|
||||
|
||||
public function parse($data) {
|
||||
$this->setId($data['id']);
|
||||
$this->setName($data['name']);
|
||||
$this->setKind($data['kind']);
|
||||
$this->setDnssec($data['dnssec']);
|
||||
$this->setAccount($data['account']);
|
||||
$this->setSerial($data['serial']);
|
||||
$this->url = $data['url'];
|
||||
if (isset($data['soa_edit']) && $data['soa_edit'] != "")
|
||||
$this->setSoaEdit($data['soa_edit']);
|
||||
if (isset($data['soa_edit_api']) && $data['soa_edit_api'] != "")
|
||||
$this->setSoaEditApi($data['soa_edit_api'], True);
|
||||
|
||||
foreach ($data['masters'] as $master) {
|
||||
$this->addMaster($master);
|
||||
}
|
||||
|
||||
if (isset($data['rrsets'])) {
|
||||
foreach ($data['rrsets'] as $rrset) {
|
||||
$toadd = new RRSet($rrset['name'], $rrset['type']);
|
||||
foreach ($rrset['comments'] as $comment) {
|
||||
$toadd->addComment($comment['content'], $comment['account'], $comment['modified_at']);
|
||||
}
|
||||
foreach ($rrset['records'] as $record) {
|
||||
$toadd->addRecord($record['content'], $record['disabled']);
|
||||
}
|
||||
$toadd->setTtl($rrset['ttl']);
|
||||
array_push($this->rrsets, $toadd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function importData($data) {
|
||||
$this->zone = $data;
|
||||
}
|
||||
|
||||
public function setKeyinfo($info) {
|
||||
$this->keyinfo = $info;
|
||||
}
|
||||
|
||||
public function addNameserver($nameserver) {
|
||||
foreach ($this->nameservers as $ns) {
|
||||
if ($nameserver == $ns) {
|
||||
throw new Exception("We already have this as a nameserver");
|
||||
}
|
||||
}
|
||||
array_push($this->nameservers, $nameserver);
|
||||
|
||||
}
|
||||
|
||||
public function setSerial($serial) {
|
||||
$this->serial = $serial;
|
||||
}
|
||||
|
||||
public function setSoaEdit($soaedit) {
|
||||
$this->soa_edit = $soaedit;
|
||||
}
|
||||
|
||||
public function setSoaEditApi($soaeditapi, $overwrite=False) {
|
||||
if (isset($this->soa_edit_api) and $this->soa_edit_api != "") {
|
||||
if ($overwrite === False) {
|
||||
return False;
|
||||
}
|
||||
}
|
||||
$this->soa_edit_api = $soaeditapi;
|
||||
}
|
||||
public function setName($name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function setKind($kind) {
|
||||
$this->kind = $kind;
|
||||
}
|
||||
|
||||
public function setAccount($account) {
|
||||
$this->account = $account;
|
||||
}
|
||||
|
||||
public function setDnssec($dnssec) {
|
||||
$this->dnssec = $dnssec;
|
||||
}
|
||||
|
||||
public function setId($id) {
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function addMaster($ip) {
|
||||
foreach ($this->masters as $master) {
|
||||
if ($ip == $master) {
|
||||
throw new Exception("We already have this as a master");
|
||||
}
|
||||
}
|
||||
array_push($this->masters, $ip);
|
||||
}
|
||||
|
||||
public function eraseMasters() {
|
||||
$this->masters = Array();
|
||||
}
|
||||
|
||||
public function addRRSet($name, $type, $content, $disabled = FALSE, $ttl = 3600, $setptr = FALSE) {
|
||||
if ($this->getRRSet($name, $type) !== FALSE) {
|
||||
throw new Exception("This rrset already exists.");
|
||||
}
|
||||
$rrset = new RRSet($name, $type, $content, $disabled, $ttl, $setptr);
|
||||
array_push($this->rrsets, $rrset);
|
||||
}
|
||||
|
||||
public function addRecord($name, $type, $content, $disabled = FALSE, $ttl = 3600, $setptr = FALSE) {
|
||||
$rrset = $this->getRRSet($name, $type);
|
||||
|
||||
if ($rrset) {
|
||||
$rrset->addRecord($content, $disabled, $setptr);
|
||||
$rrset->setTtl($ttl);
|
||||
} else {
|
||||
$this->addRRSet($name, $type, $content, $disabled, $ttl, $setptr);
|
||||
}
|
||||
|
||||
return $this->getRecord($name, $type, $content);
|
||||
}
|
||||
|
||||
public function getRecord($name, $type, $content) {
|
||||
$rrset = $this->getRRSet($name, $type);
|
||||
foreach ($rrset->exportRecords() as $record) {
|
||||
if ($record['content'] == $content) {
|
||||
$record['name'] = $rrset->name;
|
||||
$record['ttl'] = $rrset->ttl;
|
||||
$record['type'] = $rrset->type;
|
||||
$id = json_encode($record);
|
||||
$record['id'] = $id;
|
||||
return $record;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function getRRSet($name, $type) {
|
||||
foreach ($this->rrsets as $rrset) {
|
||||
if ($rrset->name == $name and $rrset->type == $type) {
|
||||
return $rrset;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
public function rrsets2records() {
|
||||
$ret = Array();
|
||||
|
||||
foreach ($this->rrsets as $rrset) {
|
||||
foreach ($rrset->exportRecords() as $record) {
|
||||
$record['name'] = $rrset->name;
|
||||
$record['ttl'] = $rrset->ttl;
|
||||
$record['type'] = $rrset->type;
|
||||
$id = json_encode($record);
|
||||
$record['id'] = $id;
|
||||
array_push($ret, $record);
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function export() {
|
||||
$ret = Array();
|
||||
$ret['account'] = $this->account;
|
||||
$ret['nameservers'] = $this->nameservers;
|
||||
$ret['kind'] = $this->kind;
|
||||
$ret['name'] = $this->name;
|
||||
if (isset($this->soa_edit) && $this->soa_edit != "") {
|
||||
$ret['soa_edit'] = $this->soa_edit;
|
||||
}
|
||||
if (isset($this->soa_edit_api) && $this->soa_edit_api != "") {
|
||||
$ret['soa_edit_api'] = $this->soa_edit_api;
|
||||
}
|
||||
if ($this->zone) {
|
||||
$ret['zone'] = $this->zone;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$ret['dnssec'] = $this->dnssec;
|
||||
if ($this->dnssec) {
|
||||
$ret['keyinfo'] = $this->keyinfo;
|
||||
}
|
||||
$ret['id'] = $this->id;
|
||||
$ret['masters'] = $this->masters;
|
||||
$ret['rrsets'] = $this->exportRRSets();
|
||||
$ret['serial'] = $this->serial;
|
||||
$ret['url'] = $this->url;
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
private function exportRRSets() {
|
||||
$ret = Array();
|
||||
foreach ($this->rrsets as $rrset) {
|
||||
array_push($ret, $rrset->export());
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
class RRSet {
|
||||
public $name;
|
||||
public $type;
|
||||
public $ttl;
|
||||
public $changetype;
|
||||
public $records;
|
||||
public $comments;
|
||||
|
||||
public function __construct($name = '', $type = '', $content = '', $disabled = FALSE, $ttl = 3600, $setptr = FALSE) {
|
||||
$this->name = $name;
|
||||
$this->type = $type;
|
||||
$this->ttl = $ttl;
|
||||
$this->changetype = 'REPLACE';
|
||||
$this->records = Array();
|
||||
$this->comments = Array();
|
||||
|
||||
if (isset($content) and $content != '') {
|
||||
$this->addRecord($content, $disabled, $setptr);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
$this->changetype = 'DELETE';
|
||||
}
|
||||
|
||||
public function setTtl($ttl) {
|
||||
$this->ttl = $ttl;
|
||||
}
|
||||
|
||||
public function setName($name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function addRecord($content, $disabled = FALSE, $setptr = FALSE) {
|
||||
foreach ($this->records as $record) {
|
||||
if ($record->content == $content) {
|
||||
throw new Exception($this->name."/".$this->type." has duplicate records.");
|
||||
}
|
||||
}
|
||||
|
||||
$record = new Record($content, $disabled, $setptr);
|
||||
array_push($this->records, $record);
|
||||
}
|
||||
|
||||
public function deleteRecord($content) {
|
||||
foreach ($this->records as $idx => $record) {
|
||||
if ($record->content == $content) {
|
||||
unset($this->records[$idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
public function addComment($content, $account, $modified_at = FALSE) {
|
||||
$comment = new Comment($content, $account, $modified_at);
|
||||
array_push($this->comments, $comment);
|
||||
}
|
||||
|
||||
public function export() {
|
||||
$ret = Array();
|
||||
$ret['comments'] = $this->exportComments();
|
||||
$ret['name'] = $this->name;
|
||||
$ret['records'] = $this->exportRecords();
|
||||
if ($this->changetype != 'DELETE') {
|
||||
$ret['ttl'] = $this->ttl;
|
||||
}
|
||||
$ret['type'] = $this->type;
|
||||
$ret['changetype'] = $this->changetype;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function exportRecords() {
|
||||
$ret = Array();
|
||||
foreach ($this->records as $record) {
|
||||
if ($this->type != "A" and $this->type != "AAAA") {
|
||||
$record->setptr = FALSE;
|
||||
}
|
||||
array_push($ret, $record->export());
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function exportComments() {
|
||||
$ret = Array();
|
||||
foreach ($this->comments as $comment) {
|
||||
array_push($ret, $comment->export());
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Record {
|
||||
public $content;
|
||||
public $disabled;
|
||||
public $setptr;
|
||||
|
||||
public function __construct($content, $disabled = FALSE, $setptr = FALSE) {
|
||||
$this->content = $content;
|
||||
$this->disabled = $disabled;
|
||||
$this->setptr = $setptr;
|
||||
}
|
||||
|
||||
public function export() {
|
||||
$ret;
|
||||
|
||||
$ret['content'] = $this->content;
|
||||
$ret['disabled'] = ( bool ) $this->disabled;
|
||||
if ($this->setptr) {
|
||||
$ret['set-ptr'] = ( bool ) TRUE;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
class Comment {
|
||||
public $content;
|
||||
public $account;
|
||||
public $modified_at;
|
||||
|
||||
public function __construct($content, $account, $modified_at) {
|
||||
$this->content = $content;
|
||||
$this->account = $account;
|
||||
$this->modified_at = $modified_at;
|
||||
}
|
||||
|
||||
public function export() {
|
||||
$ret;
|
||||
|
||||
$ret['content'] = $this->content;
|
||||
$ret['account'] = $this->account;
|
||||
$ret['modified_at'] = $this->modified_at;
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,18 +1,23 @@
|
|||
<?php
|
||||
|
||||
$apipass = ''; # The PowerDNS API-key
|
||||
$apiuser = ''; # The PowerDNS API username. Leave empty for authmethod='xapikey' (see AUTHENTICATION)
|
||||
$apipass = ''; # The PowerDNS API-user password or the PowerDNS-API key (see AUTHENTICATION)
|
||||
$apiip = ''; # The IP of the PowerDNS API
|
||||
$apiport = '8081'; # The port of the PowerDNS API
|
||||
$apivers = 0; # The version of the PowerDNS API. 0 == experimental, 1 = v1 (pdns 4.0)
|
||||
$apisid = 'localhost'; # PowerDNS's :server_id
|
||||
$apiproto = 'http'; # http | https
|
||||
$apisslverify = FALSE; # Verify SSL Certificate if using https for apiproto
|
||||
$allowzoneadd = FALSE; # Allow normal users to add zones
|
||||
$logging = TRUE;
|
||||
$allowclearlogs = TRUE; # Allow clearing of log entries
|
||||
$allowrotatelogs = FALSE;# Allow rotation to text file on server
|
||||
|
||||
# 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";
|
||||
|
||||
### AUTHENTICATION ###
|
||||
# The first versions of the PowerDNS API used the standard webserver password
|
||||
# for authentication, newer versions use an X-API-Key mechanism. NSEdit tries
|
||||
# to autodetect the method you should use, but that does affect performance.
|
||||
# For optimal performance, configure the right method.
|
||||
# (Should be 'auto', 'xapikey' or 'userpass')
|
||||
$authmethod = 'auto';
|
||||
|
||||
# If you configure this, nsedit will try to authenticate via WeFact too.
|
||||
# Debtors will be added to the sqlitedatabase with their crypted password.
|
||||
|
@ -24,13 +29,8 @@ $logsdirectory = "../etc";
|
|||
#$adminapiips = array();
|
||||
#$adminapikey = 'thisshouldbequitealongstring,youknow';
|
||||
|
||||
# Location of user-database. Make sure its writeable and not served by the webserver!
|
||||
$authdb = "../etc/pdns.users.sqlite3";
|
||||
|
||||
# Admin login and password at first start-up
|
||||
$default_admin_username = "admin";
|
||||
$default_admin_password = "admin";
|
||||
|
||||
# Set a random generated secret to enable auto-login and long living csrf tokens
|
||||
// $secret = '...';
|
||||
|
||||
|
@ -40,27 +40,19 @@ $templates[] = array(
|
|||
'name' => 'Tuxis',
|
||||
'owner' => 'username', # Set to 'public' to make it available to all users
|
||||
'records' => array(
|
||||
array(
|
||||
'name' => '',
|
||||
'type' => 'MX',
|
||||
'content' => '200 mx2.tuxis.nl.'),
|
||||
array(
|
||||
'name' => '',
|
||||
'type' => 'A',
|
||||
'content' => '1.2.3.4'),
|
||||
array(
|
||||
'name' => 'www',
|
||||
'type' => 'CNAME',
|
||||
'content' => '[zonename]')
|
||||
array(
|
||||
'name' => '',
|
||||
'type' => 'MX',
|
||||
'content' => '200 mx2.tuxis.nl')
|
||||
)
|
||||
);
|
||||
*/
|
||||
|
||||
$defaults['soa_edit'] = 'INCEPTION-INCREMENT';
|
||||
$defaults['soa_edit_api'] = 'DEFAULT';
|
||||
$defaults['soa_edit_api'] = 'INCEPTION-INCREMENT';
|
||||
$defaults['defaulttype'] = 'Master'; # Choose between 'Native' or 'Master'
|
||||
$defaults['ns'][0] = 'unconfigured.primaryns.'; # The value of the first NS-record
|
||||
$defaults['ns'][1] = 'unconfigured.secondaryns.'; # The value of the second NS-record
|
||||
$defaults['ns'][0] = 'unconfigured.primaryns'; # The value of the first NS-record
|
||||
$defaults['ns'][1] = 'unconfigured.secondaryns'; # The value of the second NS-record
|
||||
$defaults['ttl'] = 3600; # Default TTL for records
|
||||
$defaults['disabled'] = false; # Default disabled state
|
||||
|
||||
|
|
|
@ -4,11 +4,21 @@ include('config.inc.php');
|
|||
|
||||
$blocklogin = FALSE;
|
||||
|
||||
if ((!isset($apipass) or empty($apipass)) or (!isset($apiip) or empty($apiip)) or (!isset($apiport) or empty($apiport))) {
|
||||
if ((!isset($apipass) or empty($apipass)) or (!isset($apiip) or empty($apiip)) or (!isset($apiport) or empty($apiport)) or (!isset($apivers) or is_null($apivers))) {
|
||||
$errormsg = 'You need to configure your settings for the PowerDNS API. See <a href="doc/apiconf.txt">doc/apiconf.txt</a>';
|
||||
$blocklogin = TRUE;
|
||||
}
|
||||
|
||||
if (!preg_match('/^[01]$/', $apivers)) {
|
||||
$errormsg = "The value for \$apivers is incorrect your config";
|
||||
$blocklogin = TRUE;
|
||||
}
|
||||
|
||||
if (!isset($authmethod) or !preg_match('/^(xapikey|userpass|auto)$/', $authmethod)) {
|
||||
$errormsg = "The value for \$authmethod is incorrect in your config";
|
||||
$blocklogin = TRUE;
|
||||
}
|
||||
|
||||
if (!isset($apiproto) or !preg_match('/^http(s)?$/', $apiproto)) {
|
||||
$errormsg = "The value for \$apiproto is incorrect in your config. Did you configure it?";
|
||||
$blocklogin = TRUE;
|
||||
|
@ -35,12 +45,18 @@ if (isset($defaults['primaryns'])) {
|
|||
}
|
||||
|
||||
if (!isset($logo) or empty($logo)) {
|
||||
$logo = 'https://www.tuxis.nl/uploads/images/nsedit.png';
|
||||
$logo = 'http://www.tuxis.nl/uploads/images/nsedit.png';
|
||||
}
|
||||
|
||||
|
||||
/* No need to change stuf below */
|
||||
|
||||
if ($apivers == 0) {
|
||||
$apipath = "";
|
||||
} elseif ($apivers == 1) {
|
||||
$apipath = "/api/v1";
|
||||
}
|
||||
|
||||
if (function_exists('curl_init') === FALSE) {
|
||||
$errormsg = "You need PHP Curl to run nsedit";
|
||||
$blocklogin = TRUE;
|
||||
|
@ -50,7 +66,7 @@ if (class_exists('SQLite3') === FALSE) {
|
|||
$errormsg = "You need PHP SQLite3 to run nsedit";
|
||||
$blocklogin = TRUE;
|
||||
}
|
||||
|
||||
|
||||
if (function_exists('openssl_random_pseudo_bytes') === FALSE) {
|
||||
$errormsg = "You need PHP compiled with openssl to run nsedit";
|
||||
$blocklogin = TRUE;
|
||||
|
@ -59,20 +75,13 @@ if (function_exists('openssl_random_pseudo_bytes') === FALSE) {
|
|||
|
||||
$defaults['defaulttype'] = ucfirst(strtolower($defaults['defaulttype']));
|
||||
|
||||
try {
|
||||
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));
|
||||
$default_admin_username = $default_admin_username ?? "admin";
|
||||
$default_admin_password = $default_admin_password ?? "admin";
|
||||
$db->exec("INSERT INTO users (emailaddress, password, isadmin) VALUES ('".$default_admin_username."', '".crypt($default_admin_password, '$6$'.$salt)."', 1)");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
print("We have issues getting the authdb working: $e");
|
||||
$blocklogin = TRUE;
|
||||
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)");
|
||||
}
|
||||
|
||||
function string_starts_with($string, $prefix)
|
||||
|
@ -92,12 +101,10 @@ function string_ends_with($string, $suffix)
|
|||
}
|
||||
|
||||
function get_db() {
|
||||
global $authdb, $db;
|
||||
global $authdb;
|
||||
|
||||
if (!isset($db)) {
|
||||
$db = new SQLite3($authdb, SQLITE3_OPEN_READWRITE);
|
||||
$db->exec('PRAGMA foreign_keys = 1');
|
||||
}
|
||||
$db = new SQLite3($authdb, SQLITE3_OPEN_READWRITE);
|
||||
$db->exec('PRAGMA foreign_keys = 1');
|
||||
|
||||
return $db;
|
||||
}
|
||||
|
@ -119,6 +126,7 @@ function get_user_info($u) {
|
|||
$q->bindValue(1, $u);
|
||||
$result = $q->execute();
|
||||
$userinfo = $result->fetchArray(SQLITE3_ASSOC);
|
||||
$db->close();
|
||||
|
||||
return $userinfo;
|
||||
}
|
||||
|
@ -133,6 +141,7 @@ function do_db_auth($u, $p) {
|
|||
$q->bindValue(1, $u);
|
||||
$result = $q->execute();
|
||||
$userinfo = $result->fetchArray(SQLITE3_ASSOC);
|
||||
$db->close();
|
||||
|
||||
if ($userinfo and $userinfo['password'] and (crypt($p, $userinfo['password']) === $userinfo['password'])) {
|
||||
return TRUE;
|
||||
|
@ -156,16 +165,12 @@ function add_user($username, $isadmin = FALSE, $password = '') {
|
|||
$q->bindValue(2, $password, SQLITE3_TEXT);
|
||||
$q->bindValue(3, (int)(bool) $isadmin, SQLITE3_INTEGER);
|
||||
$ret = $q->execute();
|
||||
$db->close();
|
||||
|
||||
if ($isadmin) {
|
||||
writelog("Added user $username as admin.");
|
||||
} else {
|
||||
writelog("Added user $username.");
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function update_user($id, $isadmin, $password) {
|
||||
function update_user($username, $isadmin, $password) {
|
||||
if ($password && !preg_match('/\$6\$/', $password)) {
|
||||
$salt = bin2hex(openssl_random_pseudo_bytes(16));
|
||||
$password = crypt($password, '$6$'.$salt);
|
||||
|
@ -173,49 +178,30 @@ function update_user($id, $isadmin, $password) {
|
|||
|
||||
$db = get_db();
|
||||
|
||||
$q = $db->prepare('SELECT * FROM users WHERE id = ?');
|
||||
$q->bindValue(1, $id, SQLITE3_INTEGER);
|
||||
$result = $q->execute();
|
||||
$userinfo = $result->fetchArray(SQLITE3_ASSOC);
|
||||
$q->close();
|
||||
$username = $userinfo['emailaddress'];
|
||||
|
||||
if ($password) {
|
||||
$q = $db->prepare('UPDATE users SET isadmin = ?, password = ? WHERE id = ?');
|
||||
$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, $id, SQLITE3_INTEGER);
|
||||
writelog("Updating password and/or settings for $username. Admin: ".(int)(bool)$isadmin);
|
||||
$q->bindValue(3, $username, SQLITE3_TEXT);
|
||||
} else {
|
||||
$q = $db->prepare('UPDATE users SET isadmin = ? WHERE id = ?');
|
||||
$q = $db->prepare('UPDATE users SET isadmin = ? WHERE emailaddress = ?');
|
||||
$q->bindValue(1, (int)(bool)$isadmin, SQLITE3_INTEGER);
|
||||
$q->bindValue(2, $id, SQLITE3_INTEGER);
|
||||
writelog("Updating settings for $username. Admin: ".(int)(bool)$isadmin);
|
||||
$q->bindValue(2, $username, SQLITE3_TEXT);
|
||||
}
|
||||
$ret = $q->execute();
|
||||
$db->close();
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function delete_user($id) {
|
||||
$db = get_db();
|
||||
|
||||
$q = $db->prepare('SELECT * FROM users WHERE id = ?');
|
||||
$q = $db->prepare('DELETE FROM users WHERE id = ?');
|
||||
$q->bindValue(1, $id, SQLITE3_INTEGER);
|
||||
$result = $q->execute();
|
||||
$userinfo = $result->fetchArray(SQLITE3_ASSOC);
|
||||
$q->close();
|
||||
$ret = $q->execute();
|
||||
$db->close();
|
||||
|
||||
if($userinfo) {
|
||||
$q = $db->prepare('DELETE FROM users WHERE id = ?');
|
||||
$q->bindValue(1, $id, SQLITE3_INTEGER);
|
||||
$ret = $q->execute();
|
||||
|
||||
writelog("Deleted user " . $userinfo['emailaddress'] . ".");
|
||||
return $ret;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function valid_user($name) {
|
||||
|
@ -245,8 +231,6 @@ function jtable_respond($records, $method = 'multiple', $msg = 'Undefined errorm
|
|||
$jTableResult['RecordCount'] = count($records);
|
||||
}
|
||||
|
||||
$db = get_db();
|
||||
$db->close();
|
||||
header('Content-Type: application/json');
|
||||
print json_encode($jTableResult);
|
||||
exit(0);
|
||||
|
@ -255,27 +239,6 @@ function jtable_respond($records, $method = 'multiple', $msg = 'Undefined errorm
|
|||
function user_template_list() {
|
||||
global $templates;
|
||||
|
||||
if (is_dir("templates.d")) {
|
||||
if ($templdir=opendir("templates.d")) {
|
||||
while ($entry = readdir($templdir)) {
|
||||
if (!str_ends_with($entry, ".json")) {
|
||||
continue;
|
||||
}
|
||||
$f=file_get_contents("templates.d/$entry");
|
||||
if ($f === false) {
|
||||
error_log("Error reading file templates.d/$entry", 0);
|
||||
continue;
|
||||
}
|
||||
$t = json_decode($f, true);
|
||||
if ($t === null) {
|
||||
error_log("Error decoding templates.d/$entry", 0);
|
||||
continue;
|
||||
}
|
||||
array_push($templates, $t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$templatelist = array();
|
||||
foreach ($templates as $template) {
|
||||
if (is_adminuser()
|
||||
|
@ -295,109 +258,7 @@ function user_template_names() {
|
|||
return $templatenames;
|
||||
}
|
||||
|
||||
function getlogs() {
|
||||
global $logging;
|
||||
if ($logging !== TRUE)
|
||||
return;
|
||||
|
||||
$db = get_db();
|
||||
$r = $db->query('SELECT * FROM logs ORDER BY timestamp DESC');
|
||||
$ret = array();
|
||||
while ($row = $r->fetchArray(SQLITE3_ASSOC)) {
|
||||
array_push($ret, $row);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function clearlogs() {
|
||||
global $logging;
|
||||
if ($logging !== TRUE)
|
||||
return;
|
||||
|
||||
$db = get_db();
|
||||
$q = $db->query('DELETE FROM logs;');
|
||||
writelog("Logtable truncated.");
|
||||
}
|
||||
|
||||
function rotatelogs() {
|
||||
global $logging, $logsdirectory;
|
||||
if ($logging !== TRUE)
|
||||
return FALSE;
|
||||
|
||||
if(!is_dir($logsdirectory) || !is_writable($logsdirectory)) {
|
||||
writelog("Logs directory cannot be written to.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
date_default_timezone_set('UTC');
|
||||
$filename = date("Y-m-d-His") . ".json";
|
||||
$file = fopen($logsdirectory . "/" . $filename, "x");
|
||||
|
||||
if($file === FALSE) {
|
||||
writelog("Can't create file for log rotation.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(fwrite($file,json_encode(getlogs())) === FALSE) {
|
||||
writelog("Can't write to file for log rotation.");
|
||||
fclose($file);
|
||||
return FALSE;
|
||||
} else {
|
||||
fclose($file);
|
||||
clearlogs();
|
||||
return $filename;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function listrotatedlogs() {
|
||||
global $logging, $logsdirectory;
|
||||
if ($logging !== TRUE)
|
||||
return FALSE;
|
||||
|
||||
$list = scandir($logsdirectory,SCANDIR_SORT_DESCENDING);
|
||||
|
||||
if($list === FALSE) {
|
||||
writelog("Logs directory cannot read.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$list=array_filter($list,
|
||||
function ($val) {
|
||||
return(preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{6}\.json/',$val) == 1);
|
||||
}
|
||||
);
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
function writelog($line, $user=False) {
|
||||
global $logging;
|
||||
if ($logging !== TRUE)
|
||||
return;
|
||||
|
||||
if ($user === False) {
|
||||
$user = get_sess_user();
|
||||
}
|
||||
|
||||
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);
|
||||
$q->execute();
|
||||
} catch (Exception $e) {
|
||||
return jtable_respond(null, 'error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/* This function was taken from https://gist.github.com/rsky/5104756 to make
|
||||
it available on older php versions. Thanks! */
|
||||
|
|
|
@ -9,13 +9,11 @@ global $current_user;
|
|||
$current_user = false;
|
||||
|
||||
// session startup
|
||||
function _set_current_user($username, $userid, $localauth = true, $is_admin = false, $has_csrf_token = false, $is_api = false) {
|
||||
function _set_current_user($username, $is_admin = false, $has_csrf_token = false, $is_api = false) {
|
||||
global $current_user;
|
||||
|
||||
$current_user = array(
|
||||
'username' => $username,
|
||||
'id' => $userid,
|
||||
'localauth' => $localauth,
|
||||
'is_admin' => $is_admin,
|
||||
'has_csrf_token' => $has_csrf_token,
|
||||
'is_api' => $is_api,
|
||||
|
@ -52,7 +50,7 @@ function _check_csrf_token($user) {
|
|||
}
|
||||
|
||||
define('CSRF_TOKEN', $csrf_token);
|
||||
header("X-CSRF-Token: {$csrf_token}");
|
||||
header("X-CSRF-Token: ${csrf_token}");
|
||||
}
|
||||
|
||||
function enc_secret($message) {
|
||||
|
@ -122,13 +120,13 @@ function dec_secret($code) {
|
|||
|
||||
function _unset_cookie($name) {
|
||||
$is_ssl = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off';
|
||||
setcookie($name, "", -1, "", "", $is_ssl);
|
||||
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, "", "", $is_ssl);
|
||||
setcookie('NSEDIT_AUTOLOGIN', $value, time()+60*60*24*30, null, null, $is_ssl);
|
||||
}
|
||||
|
||||
function try_login() {
|
||||
|
@ -152,7 +150,6 @@ function _try_login($username, $password) {
|
|||
global $wefactapiurl, $wefactapikey;
|
||||
|
||||
if (!valid_user($username)) {
|
||||
writelog("Illegal username at login!", $username);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -161,7 +158,6 @@ function _try_login($username, $password) {
|
|||
if (isset($wefactapiurl) && isset($wefactapikey)) {
|
||||
$wefact = do_wefact_auth($username, $password);
|
||||
if (false === $wefact ) {
|
||||
writelog("Failed Wefact login!", $username);
|
||||
return false;
|
||||
}
|
||||
if (-1 !== $wefact) {
|
||||
|
@ -170,16 +166,14 @@ function _try_login($username, $password) {
|
|||
}
|
||||
|
||||
if ($do_local_auth && !do_db_auth($username, $password)) {
|
||||
writelog("Failed login!", $username);
|
||||
return false;
|
||||
}
|
||||
|
||||
$user = get_user_info($username);
|
||||
if (!$user) {
|
||||
writelog("Failed to find user!", $username);
|
||||
return false;
|
||||
} else {
|
||||
_set_current_user($username, $user['id'], (bool) $do_local_auth, (bool) $user['isadmin']);
|
||||
_set_current_user($username, (bool) $user['isadmin']);
|
||||
|
||||
if (session_id()) {
|
||||
session_unset();
|
||||
|
@ -189,8 +183,6 @@ function _try_login($username, $password) {
|
|||
session_regenerate_id(true) or die('session failure: regenerated id failed');
|
||||
session_unset();
|
||||
$_SESSION['username'] = $username;
|
||||
$_SESSION['localauth'] = $do_local_auth;
|
||||
$_SESSION['userid'] = $user['id'];
|
||||
|
||||
# requires session:
|
||||
_check_csrf_token($user);
|
||||
|
@ -210,7 +202,7 @@ function _check_session() {
|
|||
and $_POST['adminapikey'] === $adminapikey)
|
||||
{
|
||||
# Allow this request, fake that we're logged in as user.
|
||||
return _set_current_user('admin', 1, false, true, true, true);
|
||||
return _set_current_user('admin', true, true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -226,7 +218,7 @@ function _check_session() {
|
|||
session_destroy();
|
||||
session_unset();
|
||||
} else {
|
||||
_set_current_user($_SESSION['username'], $_SESSION['userid'], (bool) $_SESSION['localauth'], (bool) $user['isadmin']);
|
||||
_set_current_user($_SESSION['username'], (bool) $user['isadmin']);
|
||||
_check_csrf_token($user);
|
||||
return;
|
||||
}
|
||||
|
@ -285,16 +277,6 @@ function get_sess_user() {
|
|||
return $current_user ? $current_user['username'] : null;
|
||||
}
|
||||
|
||||
function get_sess_userid() {
|
||||
global $current_user;
|
||||
return $current_user ? $current_user['id'] : null;
|
||||
}
|
||||
|
||||
function has_local_auth() {
|
||||
global $current_user;
|
||||
return $current_user ? $current_user['localauth'] : null;
|
||||
}
|
||||
|
||||
function logout() {
|
||||
@session_destroy();
|
||||
@session_unset();
|
||||
|
|
706
index.php
706
index.php
|
@ -6,12 +6,6 @@ include_once('includes/misc.inc.php');
|
|||
|
||||
global $errormsg, $blocklogin;
|
||||
|
||||
$docroot = $_SERVER['DOCUMENT_ROOT'];
|
||||
if (preg_match("@$docroot@", $authdb) == 1) {
|
||||
$blocklogin = TRUE;
|
||||
$errormsg = "You authdb is in your document root and probably downloadable. Please move it to a safe location!";
|
||||
}
|
||||
|
||||
if (isset($_GET['logout']) or isset($_POST['logout'])) {
|
||||
logout();
|
||||
header("Location: index.php");
|
||||
|
@ -26,7 +20,7 @@ if (!is_logged_in() and isset($_POST['formname']) and $_POST['formname'] === "lo
|
|||
|
||||
if (is_logged_in() and isset($_POST['formname']) and $_POST['formname'] === "changepwform") {
|
||||
if (get_sess_user() == $_POST['username']) {
|
||||
if (!update_user(get_sess_userid(), is_adminuser(), $_POST['password'])) {
|
||||
if (!update_user(get_sess_user(), is_adminuser(), $_POST['password'])) {
|
||||
$errormsg = "Unable to update password!\n";
|
||||
}
|
||||
} else {
|
||||
|
@ -119,47 +113,6 @@ if ($blocklogin === TRUE) {
|
|||
<div id="wrap">
|
||||
<div id="dnssecinfo">
|
||||
</div>
|
||||
<div id="clearlogs" style="display: none;">
|
||||
Are you sure you want to clear the current logs? Maybe download them
|
||||
first<?php if($allowrotatelogs) { ?>, or use "Rotate logs" to save
|
||||
them on the server<?php } ?>?
|
||||
</div>
|
||||
<div id="rotatelogs" style="display: none;">
|
||||
Are you sure you want to rotate the current logs?
|
||||
</div>
|
||||
<div id="searchlogs" style="display: none; text-align: right;">
|
||||
<table border="0">
|
||||
<tr><td>User:</td><td><input type="text" id ="searchlogs-user"><br></td></tr>
|
||||
<tr><td>Log Entry:</td><td><input type="text" id ="searchlogs-entry"></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="searchzone" style="display: none; text-align: right;">
|
||||
<table border="0">
|
||||
<tr><td>Label:</td><td><input type="text" id ="searchzone-label"><br></td></tr>
|
||||
<tr><td>Type:</td><td style="text-align: left;"><select id="searchzone-type">
|
||||
<option value=""></option>
|
||||
<option value="A">A</option>
|
||||
<option value="AAAA">AAAA</option>
|
||||
<option value="CERT">CERT</option>
|
||||
<option value="CNAME">CNAME</option>
|
||||
<option value="ALIAS">ALIAS</option>
|
||||
<option value="LOC">LOC</option>
|
||||
<option value="MX">MX</option>
|
||||
<option value="NAPTR">NAPTR</option>
|
||||
<option value="NS">NS</option>
|
||||
<option value="PTR">PTR</option>
|
||||
<option value="SOA">SOA</option>
|
||||
<option value="SPF">SPF</option>
|
||||
<option value="SRV">SRV</option>
|
||||
<option value="SSHFP">SSHFP</option>
|
||||
<option value="TLSA">TLSA</option>
|
||||
<option value="CAA">CAA</option>
|
||||
<option value="TXT">TXT</option>
|
||||
<option value="SMIMEA">SMIMEA</option>
|
||||
</select><br></td></tr>
|
||||
<tr><td>Content:</td><td><input type="text" id ="searchzone-content"></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="menu" class="jtable-main-container <?php if ($menutype === 'horizontal') { ?>horizontal<?php } ?>">
|
||||
<div class="jtable-title menu-title">
|
||||
<div class="jtable-title-text">
|
||||
|
@ -170,7 +123,6 @@ if ($blocklogin === TRUE) {
|
|||
<li><a href="#" id="zoneadmin">Zones</a></li>
|
||||
<?php if (is_adminuser()) { ?>
|
||||
<li><a href="#" id="useradmin">Users</a></li>
|
||||
<li><a href="#" id="logadmin">Logs</a></li>
|
||||
<?php } ?>
|
||||
<li><a href="#" id="aboutme">About me</a></li>
|
||||
<li><a href="index.php?logout=1">Logout</a></li>
|
||||
|
@ -181,11 +133,8 @@ if ($blocklogin === TRUE) {
|
|||
}
|
||||
?>
|
||||
<div id="zones">
|
||||
<?php if ($allowzoneadd === TRUE) { ?>
|
||||
<div style="display: none;" id="ImportZone"></div>
|
||||
<?php } ?>
|
||||
<?php if (is_adminuser()) { ?>
|
||||
<div style="display: none;" id="CloneZone"></div>
|
||||
<?php if (is_adminuser() or $allowzoneadd === TRUE) { ?>
|
||||
<div style="visibility: hidden;" id="ImportZone"></div>
|
||||
<?php } ?>
|
||||
<div class="tables" id="MasterZones">
|
||||
<div class="searchbar" id="searchbar">
|
||||
|
@ -198,27 +147,8 @@ if ($blocklogin === TRUE) {
|
|||
<div id="users">
|
||||
<div class="tables" id="Users"></div>
|
||||
</div>
|
||||
<div id="logs">
|
||||
<div class="tables" id="Logs"></div>
|
||||
<?php if($allowrotatelogs) { ?>
|
||||
<br>Log entries being viewed:
|
||||
<select id="logfile">
|
||||
<option value="">(Current logs)</option>
|
||||
<?php
|
||||
$logfiles=listrotatedlogs();
|
||||
if($logfiles !== FALSE) {
|
||||
foreach ($logfiles as $filename) {
|
||||
echo '<option value="' . $filename . '">' . str_replace(".json","",$filename) . "</option>\n";
|
||||
}
|
||||
}
|
||||
?></select>
|
||||
<?php } else { ?>
|
||||
<input type="hidden" id="logfile" value="">
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (has_local_auth()) { ?>
|
||||
<div id="AboutMe">
|
||||
<div class="tables">
|
||||
<p>Hi <?php echo get_sess_user(); ?>. You can change your password here.</p>
|
||||
|
@ -243,11 +173,9 @@ if ($blocklogin === TRUE) {
|
|||
</tr>
|
||||
</table>
|
||||
<input type="hidden" name="formname" value="changepwform">
|
||||
<input type="hidden" name="id" value="<?php echo get_sess_userid(); ?>">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
window.csrf_token = '<?php echo CSRF_TOKEN ?>';
|
||||
|
@ -296,7 +224,7 @@ function displayDnssecIcon(zone) {
|
|||
function displayExportIcon(zone) {
|
||||
var $img = $('<img class="list clickme" src="img/export.png" title="Export zone" />');
|
||||
$img.click(function () {
|
||||
var $zexport = $.getJSON("zones.php?zoneid="+zone.record.id+"&action=export", function(data) {
|
||||
var $zexport = $.getJSON("zones.php?zone="+zone.record.name+"&action=export", function(data) {
|
||||
blob = new Blob([data.Record.zone], { type: 'text/plain' });
|
||||
var dl = document.createElement('a');
|
||||
dl.addEventListener('click', function(ev) {
|
||||
|
@ -323,11 +251,7 @@ function displayContent(fieldName, zone) {
|
|||
var zspan = $('<span class="lightgrey">').text(zone);
|
||||
return lspan.add(zspan);
|
||||
} else {
|
||||
var text = data.record[fieldName];
|
||||
if (typeof data.record[fieldName] == 'boolean') {
|
||||
text == false ? text = 'No' : text = 'Yes';
|
||||
}
|
||||
return $('<span>').text(text);
|
||||
return $('<span>').text(data.record[fieldName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -381,16 +305,16 @@ $(document).ready(function () {
|
|||
listClass: 'dnssec'
|
||||
},
|
||||
<?php if (is_adminuser()) { ?>
|
||||
account: {
|
||||
title: 'Account',
|
||||
owner: {
|
||||
title: 'Owner',
|
||||
width: '8%',
|
||||
display: displayContent('account'),
|
||||
display: displayContent('owner'),
|
||||
options: function(data) {
|
||||
return 'users.php?action=listoptions&e='+$epoch;
|
||||
},
|
||||
defaultValue: 'admin',
|
||||
inputClass: 'account',
|
||||
listClass: 'account'
|
||||
inputClass: 'owner',
|
||||
listClass: 'owner'
|
||||
},
|
||||
<?php } ?>
|
||||
kind: {
|
||||
|
@ -439,7 +363,7 @@ $(document).ready(function () {
|
|||
title: 'Records in ' + zone.record.name,
|
||||
openChildAsAccordion: true,
|
||||
actions: {
|
||||
listAction: 'zones.php?action=listrecords&zoneid=' + zone.record.id
|
||||
listAction: 'zones.php?action=listrecords&zoneurl=' + zone.record.url
|
||||
},
|
||||
fields: {
|
||||
name: {
|
||||
|
@ -504,227 +428,17 @@ $(document).ready(function () {
|
|||
hoverAnimation: true,
|
||||
hoverAnimationDuration: 60,
|
||||
hoverAnimationEasing: undefined,
|
||||
items: [
|
||||
<?php if ($allowzoneadd === TRUE) { ?>
|
||||
{
|
||||
icon: 'jtable/lib/themes/metro/add.png',
|
||||
text: 'Import a new zone',
|
||||
click: function() {
|
||||
$('#ImportZone').jtable('showCreateForm');
|
||||
}
|
||||
},
|
||||
items: [{
|
||||
<?php if (is_adminuser() or $allowzoneadd === TRUE) { ?>
|
||||
icon: 'jtable/lib/themes/metro/add.png',
|
||||
text: 'Import a new zone',
|
||||
click: function() {
|
||||
$('#ImportZone').jtable('showCreateForm');
|
||||
}
|
||||
<?php } ?>
|
||||
<?php if (is_adminuser()) { ?>
|
||||
{
|
||||
icon: 'jtable/lib/themes/metro/add.png',
|
||||
text: 'Clone a zone',
|
||||
click: function() {
|
||||
$('#CloneZone').jtable('showCreateForm');
|
||||
}
|
||||
},
|
||||
<?php } ?>
|
||||
],
|
||||
}],
|
||||
},
|
||||
sorting: false,
|
||||
selecting: true,
|
||||
selectOnRowClick: true,
|
||||
selectionChanged: function (data) {
|
||||
var $selectedRows = $('#MasterZones').jtable('selectedRows');
|
||||
$selectedRows.each(function () {
|
||||
var zone = $(this).data('record');
|
||||
$('#MasterZones').jtable('openChildTable',
|
||||
$(this).closest('tr'), {
|
||||
title: 'Records in ' + zone.name,
|
||||
messages: {
|
||||
addNewRecord: 'Add to ' + zone.name,
|
||||
noDataAvailable: 'No records for ' + zone.name
|
||||
},
|
||||
toolbar: {
|
||||
items: [
|
||||
{
|
||||
text: 'Search zone',
|
||||
click: function() {
|
||||
$("#searchzone").dialog({
|
||||
modal: true,
|
||||
title: "Search zone for ...",
|
||||
width: 'auto',
|
||||
buttons: {
|
||||
Search: function() {
|
||||
$( this ).dialog( 'close' );
|
||||
opentable.find('.jtable-title-text').text(opentableTitle + " (filtered)");
|
||||
opentable.jtable('load', {
|
||||
label: $('#searchzone-label').val(),
|
||||
type: $('#searchzone-type').val(),
|
||||
content: $('#searchzone-content').val()
|
||||
});
|
||||
},
|
||||
Reset: function() {
|
||||
$('#searchzone-label').val('');
|
||||
$('#searchzone-type').val('');
|
||||
$('#searchzone-content').val('');
|
||||
$( this ).dialog( 'close' );
|
||||
opentable.find('.jtable-title-text').text(opentableTitle);
|
||||
opentable.jtable('load');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
paging: true,
|
||||
sorting: true,
|
||||
pageSize: 20,
|
||||
openChildAsAccordion: true,
|
||||
actions: {
|
||||
listAction: 'zones.php?action=listrecords&zoneid=' + zone.id,
|
||||
createAction: 'zones.php?action=createrecord&zoneid=' + zone.id,
|
||||
deleteAction: 'zones.php?action=deleterecord&zoneid=' + zone.id,
|
||||
updateAction: 'zones.php?action=editrecord&zoneid=' + zone.id
|
||||
},
|
||||
fields: {
|
||||
domid: {
|
||||
create: true,
|
||||
type: 'hidden',
|
||||
defaultValue: zone.id
|
||||
},
|
||||
id: {
|
||||
key: true,
|
||||
type: 'hidden',
|
||||
create: false,
|
||||
edit: false,
|
||||
list: false
|
||||
},
|
||||
domain: {
|
||||
create: true,
|
||||
type: 'hidden',
|
||||
defaultValue: zone.name
|
||||
},
|
||||
name: {
|
||||
title: 'Label',
|
||||
width: '7%',
|
||||
sorting: true,
|
||||
create: true,
|
||||
display: displayContent('name', zone.name),
|
||||
inputClass: 'name',
|
||||
listClass: 'name'
|
||||
},
|
||||
type: {
|
||||
title: 'Type',
|
||||
width: '2%',
|
||||
options: function() {
|
||||
zonename = new String(zone.name);
|
||||
if (zonename.match(/(\.in-addr|\.ip6)\.arpa/)) {
|
||||
return {
|
||||
'PTR': 'PTR',
|
||||
'NS': 'NS',
|
||||
'MX': 'MX',
|
||||
'TXT': 'TXT',
|
||||
'SOA': 'SOA',
|
||||
'A': 'A',
|
||||
'AAAA': 'AAAA',
|
||||
'CERT': 'CERT',
|
||||
'CNAME': 'CNAME',
|
||||
'ALIAS': 'ALIAS',
|
||||
'LOC': 'LOC',
|
||||
'NAPTR': 'NAPTR',
|
||||
'SPF': 'SPF',
|
||||
'SRV': 'SRV',
|
||||
'SSHFP': 'SSHFP',
|
||||
'TLSA': 'TLSA',
|
||||
'CAA': 'CAA',
|
||||
'DNAME': 'DNAME',
|
||||
'DS': 'DS',
|
||||
'SMIMEA': 'SMIMEA'
|
||||
};
|
||||
}
|
||||
return {
|
||||
'A': 'A',
|
||||
'AAAA': 'AAAA',
|
||||
'CERT': 'CERT',
|
||||
'CNAME': 'CNAME',
|
||||
'DNAME': 'DNAME',
|
||||
'ALIAS': 'ALIAS',
|
||||
'DS': 'DS',
|
||||
'LOC': 'LOC',
|
||||
'MX': 'MX',
|
||||
'NAPTR': 'NAPTR',
|
||||
'NS': 'NS',
|
||||
'PTR': 'PTR',
|
||||
'SOA': 'SOA',
|
||||
'SPF': 'SPF',
|
||||
'SRV': 'SRV',
|
||||
'SSHFP': 'SSHFP',
|
||||
'TLSA': 'TLSA',
|
||||
'CAA': 'CAA',
|
||||
'TXT': 'TXT',
|
||||
'SMIMEA': 'SMIMEA'
|
||||
};
|
||||
},
|
||||
display: displayContent('type'),
|
||||
create: true,
|
||||
inputClass: 'type',
|
||||
listClass: 'type'
|
||||
},
|
||||
content: {
|
||||
title: 'Content',
|
||||
width: '30%',
|
||||
create: true,
|
||||
sorting: true,
|
||||
display: displayContent('content'),
|
||||
inputClass: 'content',
|
||||
listClass: 'content'
|
||||
},
|
||||
ttl: {
|
||||
title: 'TTL',
|
||||
width: '2%',
|
||||
create: true,
|
||||
sorting: false,
|
||||
display: displayContent('ttl'),
|
||||
defaultValue: '<?php echo $defaults['ttl']; ?>',
|
||||
inputClass: 'ttl',
|
||||
listClass: 'ttl'
|
||||
},
|
||||
setptr: {
|
||||
title: 'Set PTR Record',
|
||||
width: '2%',
|
||||
list: false,
|
||||
create: true,
|
||||
defaultValue: 'false',
|
||||
inputClass: 'setptr',
|
||||
listClass: 'setptr',
|
||||
options: function() {
|
||||
return {
|
||||
'0': 'No',
|
||||
'1': 'Yes',
|
||||
};
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
title: 'Disabled',
|
||||
width: '2%',
|
||||
create: true,
|
||||
sorting: false,
|
||||
display: displayContent('disabled'),
|
||||
defaultValue: '<?php echo $defaults['disabled'] ? 'No' : 'Yes'; ?>',
|
||||
inputClass: 'disabled',
|
||||
listClass: 'disabled',
|
||||
options: function() {
|
||||
return {
|
||||
'0': 'No',
|
||||
'1': 'Yes',
|
||||
};
|
||||
},
|
||||
},
|
||||
}
|
||||
}, function (data) {
|
||||
opentable=data.childTable;
|
||||
opentableTitle=opentable.find('.jtable-title-text').text();
|
||||
data.childTable.jtable('load');
|
||||
});
|
||||
});
|
||||
},
|
||||
openChildAsAccordion: true,
|
||||
actions: {
|
||||
listAction: 'zones.php?action=list',
|
||||
|
@ -758,16 +472,16 @@ $(document).ready(function () {
|
|||
listClass: 'dnssec'
|
||||
},
|
||||
<?php if (is_adminuser()) { ?>
|
||||
account: {
|
||||
title: 'Account',
|
||||
owner: {
|
||||
title: 'Owner',
|
||||
width: '8%',
|
||||
display: displayContent('account'),
|
||||
display: displayContent('owner'),
|
||||
options: function(data) {
|
||||
return 'users.php?action=listoptions&e='+$epoch;
|
||||
},
|
||||
defaultValue: 'admin',
|
||||
inputClass: 'account',
|
||||
listClass: 'account'
|
||||
inputClass: 'owner',
|
||||
listClass: 'owner'
|
||||
},
|
||||
<?php } ?>
|
||||
kind: {
|
||||
|
@ -819,6 +533,143 @@ $(document).ready(function () {
|
|||
inputClass: 'serial',
|
||||
listClass: 'serial'
|
||||
},
|
||||
records: {
|
||||
width: '5%',
|
||||
title: 'Records',
|
||||
edit: false,
|
||||
create: false,
|
||||
display: function (zone) {
|
||||
var $img = $('<img class="list" src="img/list.png" title="Records" />');
|
||||
$img.click(function () {
|
||||
$('#MasterZones').jtable('openChildTable',
|
||||
$img.closest('tr'), {
|
||||
title: 'Records in ' + zone.record.name,
|
||||
messages: {
|
||||
addNewRecord: 'Add to ' + zone.record.name,
|
||||
noDataAvailable: 'No records for ' + zone.record.name
|
||||
},
|
||||
paging: true,
|
||||
pageSize: 20,
|
||||
openChildAsAccordion: true,
|
||||
actions: {
|
||||
listAction: 'zones.php?action=listrecords&zoneurl=' + zone.record.url,
|
||||
createAction: 'zones.php?action=createrecord&zoneurl=' + zone.record.url,
|
||||
deleteAction: 'zones.php?action=deleterecord&zoneurl=' + zone.record.url,
|
||||
updateAction: 'zones.php?action=editrecord&zoneurl=' + zone.record.url
|
||||
},
|
||||
fields: {
|
||||
domid: {
|
||||
create: true,
|
||||
type: 'hidden',
|
||||
defaultValue: zone.record.id
|
||||
},
|
||||
id: {
|
||||
key: true,
|
||||
type: 'hidden',
|
||||
create: false,
|
||||
edit: false,
|
||||
list: false
|
||||
},
|
||||
domain: {
|
||||
create: true,
|
||||
type: 'hidden',
|
||||
defaultValue: zone.record.name
|
||||
},
|
||||
name: {
|
||||
title: 'Label',
|
||||
width: '7%',
|
||||
create: true,
|
||||
display: displayContent('name', zone.record.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 {
|
||||
'PTR': 'PTR',
|
||||
'NS': 'NS',
|
||||
'MX': 'MX',
|
||||
'TXT': 'TXT',
|
||||
'SOA': 'SOA',
|
||||
'A': 'A',
|
||||
'AAAA': 'AAAA',
|
||||
'CERT': 'CERT',
|
||||
'CNAME': 'CNAME',
|
||||
'LOC': 'LOC',
|
||||
'NAPTR': 'NAPTR',
|
||||
'SPF': 'SPF',
|
||||
'SRV': 'SRV',
|
||||
'SSHFP': 'SSHFP',
|
||||
'TLSA': 'TLSA',
|
||||
};
|
||||
}
|
||||
return {
|
||||
'A': 'A',
|
||||
'AAAA': 'AAAA',
|
||||
'CERT': 'CERT',
|
||||
'CNAME': 'CNAME',
|
||||
'LOC': 'LOC',
|
||||
'MX': 'MX',
|
||||
'NAPTR': 'NAPTR',
|
||||
'NS': 'NS',
|
||||
'PTR': 'PTR',
|
||||
'SOA': 'SOA',
|
||||
'SPF': 'SPF',
|
||||
'SRV': 'SRV',
|
||||
'SSHFP': 'SSHFP',
|
||||
'TLSA': 'TLSA',
|
||||
'TXT': 'TXT',
|
||||
};
|
||||
},
|
||||
display: displayContent('type'),
|
||||
create: true,
|
||||
inputClass: 'type',
|
||||
listClass: 'type'
|
||||
},
|
||||
content: {
|
||||
title: 'Content',
|
||||
width: '30%',
|
||||
create: true,
|
||||
display: displayContent('content'),
|
||||
inputClass: 'content',
|
||||
listClass: 'content'
|
||||
},
|
||||
ttl: {
|
||||
title: 'TTL',
|
||||
width: '2%',
|
||||
create: true,
|
||||
display: displayContent('ttl'),
|
||||
defaultValue: '<?php echo $defaults['ttl']; ?>',
|
||||
inputClass: 'ttl',
|
||||
listClass: 'ttl'
|
||||
},
|
||||
disabled: {
|
||||
title: 'Disabled',
|
||||
width: '2%',
|
||||
create: true,
|
||||
display: displayContent('disabled'),
|
||||
defaultValue: '<?php echo $defaults['disabled'] ? 'false' : 'true'; ?>',
|
||||
inputClass: 'disabled',
|
||||
listClass: 'disabled',
|
||||
options: function() {
|
||||
return {
|
||||
'0': 'false',
|
||||
'1': 'true',
|
||||
};
|
||||
},
|
||||
},
|
||||
}
|
||||
}, function (data) {
|
||||
data.childTable.jtable('load');
|
||||
})
|
||||
});
|
||||
return $img;
|
||||
}
|
||||
},
|
||||
exportzone: {
|
||||
title: '',
|
||||
width: '1%',
|
||||
|
@ -844,13 +695,13 @@ $(document).ready(function () {
|
|||
inputClass: 'domain'
|
||||
},
|
||||
<?php if (is_adminuser()) { ?>
|
||||
account: {
|
||||
title: 'Account',
|
||||
owner: {
|
||||
title: 'Owner',
|
||||
options: function(data) {
|
||||
return 'users.php?action=listoptions&e='+$epoch;
|
||||
},
|
||||
defaultValue: 'admin',
|
||||
inputClass: 'account'
|
||||
inputClass: 'owner'
|
||||
},
|
||||
<?php } ?>
|
||||
kind: {
|
||||
|
@ -870,7 +721,7 @@ $(document).ready(function () {
|
|||
type: 'checkbox',
|
||||
values: {'0': 'No', '1': 'Yes'},
|
||||
defaultValue: 1,
|
||||
inputClass: 'overwrite_nameserver'
|
||||
inputClass: 'overwrite_namerserver'
|
||||
},
|
||||
nameserver: {
|
||||
title: 'Nameservers',
|
||||
|
@ -891,50 +742,6 @@ $(document).ready(function () {
|
|||
}
|
||||
|
||||
});
|
||||
$('#CloneZone').jtable({
|
||||
title: 'Clone zone',
|
||||
actions: {
|
||||
createAction: 'zones.php?action=clone'
|
||||
},
|
||||
fields: {
|
||||
id: {
|
||||
key: true,
|
||||
type: 'hidden'
|
||||
},
|
||||
sourcename: {
|
||||
title: 'Source domain',
|
||||
options: function(data) {
|
||||
return 'zones.php?action=formzonelist&e='+$epoch;
|
||||
},
|
||||
inputClass: 'sourcename'
|
||||
},
|
||||
destname: {
|
||||
title: 'Domain',
|
||||
inputClass: 'destname'
|
||||
},
|
||||
account: {
|
||||
title: 'Account',
|
||||
options: function(data) {
|
||||
return 'users.php?action=listoptions&e='+$epoch;
|
||||
},
|
||||
defaultValue: 'admin',
|
||||
inputClass: 'account'
|
||||
},
|
||||
kind: {
|
||||
title: 'Type',
|
||||
options: {'Native': 'Native', 'Master': 'Master'},
|
||||
defaultValue: '<?php echo $defaults['defaulttype']; ?>',
|
||||
edit: false,
|
||||
inputClass: 'type'
|
||||
},
|
||||
},
|
||||
recordAdded: function() {
|
||||
$("#MasterZones").jtable('load');
|
||||
$("#SlaveZones").jtable('load');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$('#domsearch').addClear({
|
||||
onClear: function() { $('#MasterZones').jtable('load'); }
|
||||
});
|
||||
|
@ -965,41 +772,26 @@ $(document).ready(function () {
|
|||
});
|
||||
|
||||
<?php if (is_adminuser()) { ?>
|
||||
$('#logs').hide();
|
||||
$('#Users').hide();
|
||||
$('#AboutMe').hide();
|
||||
$('#aboutme').click(function () {
|
||||
$('#logs').hide();
|
||||
$('#Users').hide();
|
||||
$('#MasterZones').hide();
|
||||
$('#SlaveZones').hide();
|
||||
$('#AboutMe').show();
|
||||
});
|
||||
$('#useradmin').click(function () {
|
||||
$('#logs').hide();
|
||||
$('#Users').show();
|
||||
$('#MasterZones').hide();
|
||||
$('#SlaveZones').hide();
|
||||
$('#AboutMe').hide();
|
||||
$('#Users').jtable('load');
|
||||
$('#Users').show();
|
||||
});
|
||||
$('#zoneadmin').click(function () {
|
||||
$('#logs').hide();
|
||||
$('#Users').hide();
|
||||
$('#AboutMe').hide();
|
||||
$('#MasterZones').show();
|
||||
$('#SlaveZones').show();
|
||||
});
|
||||
$('#logadmin').click(function () {
|
||||
$('#Users').hide();
|
||||
$('#AboutMe').hide();
|
||||
$('#MasterZones').hide();
|
||||
$('#SlaveZones').hide();
|
||||
$('#Logs').jtable('load', {
|
||||
logfile: $('#logfile').val()
|
||||
});
|
||||
$('#logs').show();
|
||||
});
|
||||
$('#Users').jtable({
|
||||
title: 'Users',
|
||||
paging: true,
|
||||
|
@ -1024,7 +816,6 @@ $(document).ready(function () {
|
|||
title: 'User',
|
||||
display: displayContent('emailaddress'),
|
||||
inputClass: 'emailaddress',
|
||||
edit: false,
|
||||
listClass: 'emailaddress'
|
||||
},
|
||||
password: {
|
||||
|
@ -1047,170 +838,7 @@ $(document).ready(function () {
|
|||
$("#SlaveZones").jtable('reload');
|
||||
}
|
||||
});
|
||||
|
||||
$('#Logs').jtable({
|
||||
title: 'Logs',
|
||||
paging: true,
|
||||
pageSize: 20,
|
||||
sorting: false,
|
||||
actions: {
|
||||
listAction: 'logs.php?action=list'
|
||||
},
|
||||
messages: {
|
||||
deleteConfirmation: 'This entry will be deleted. Are you sure?'
|
||||
},
|
||||
toolbar: {
|
||||
hoverAnimation: true,
|
||||
hoverAnimationDuration: 60,
|
||||
hoverAnimationEasing: undefined,
|
||||
items: [
|
||||
{
|
||||
text: 'Search logs',
|
||||
click: function() {
|
||||
$("#searchlogs").dialog({
|
||||
modal: true,
|
||||
title: "Search logs for ...",
|
||||
width: 'auto',
|
||||
buttons: {
|
||||
Search: function() {
|
||||
$( this ).dialog( 'close' );
|
||||
$('#Logs').find('.jtable-title-text').text('Logs (filtered)');
|
||||
$('#Logs').jtable('load', {
|
||||
logfile: $('#logfile').val(),
|
||||
user: $('#searchlogs-user').val(),
|
||||
entry: $('#searchlogs-entry').val()
|
||||
});
|
||||
},
|
||||
Reset: function() {
|
||||
$('#searchlogs-user').val('');
|
||||
$('#searchlogs-entry').val('');
|
||||
$( this ).dialog( 'close' );
|
||||
$('#Logs').find('.jtable-title-text').text('Logs');
|
||||
$('#Logs').jtable('load', {
|
||||
logfile: $('#logfile').val()
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
<?php if($allowrotatelogs === TRUE) { ?>
|
||||
{
|
||||
text: 'Rotate logs',
|
||||
click: function() {
|
||||
$("#rotatelogs").dialog({
|
||||
modal: true,
|
||||
title: "Rotate logs",
|
||||
width: 'auto',
|
||||
buttons: {
|
||||
Ok: function() {
|
||||
$.get("logs.php?action=rotate");
|
||||
$( this ).dialog( "close" );
|
||||
$('#logfile').val('');
|
||||
$('#Logs').jtable('load');
|
||||
},
|
||||
Cancel: function() {
|
||||
$( this ).dialog( "close" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
<?php } ?>
|
||||
<?php if($allowclearlogs === TRUE) { ?>
|
||||
{
|
||||
icon: 'img/delete_inverted.png',
|
||||
text: 'Clear logs',
|
||||
click: function() {
|
||||
$("#clearlogs").dialog({
|
||||
modal: true,
|
||||
title: "Clear all logs",
|
||||
width: 'auto',
|
||||
buttons: {
|
||||
Ok: function() {
|
||||
$.get("logs.php?action=clear");
|
||||
$( this ).dialog( "close" );
|
||||
$('#logfile').val('');
|
||||
$('#Logs').jtable('load');
|
||||
},
|
||||
Cancel: function() {
|
||||
$( this ).dialog( "close" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
<?php } ?>
|
||||
{
|
||||
icon: 'img/export.png',
|
||||
text: 'Download logs',
|
||||
click: function () {
|
||||
var $zexport = $.get("logs.php?action=export&logfile=" + $('#logfile').val(), function(data) {
|
||||
console.log(data);
|
||||
blob = new Blob([data], { type: 'text/plain' });
|
||||
var dl = document.createElement('a');
|
||||
dl.addEventListener('click', function(ev) {
|
||||
dl.href = URL.createObjectURL(blob);
|
||||
dl.download = $('#logfile').val() == "" ? 'nseditlogs.txt':$('#logfile').val() + ".txt";
|
||||
}, false);
|
||||
|
||||
if (document.createEvent) {
|
||||
var event = document.createEvent("MouseEvents");
|
||||
event.initEvent("click", true, true);
|
||||
dl.dispatchEvent(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
fields: {
|
||||
id: {
|
||||
title: 'key',
|
||||
key: true,
|
||||
type: 'hidden'
|
||||
},
|
||||
user: {
|
||||
title: 'User',
|
||||
width: '10%',
|
||||
display: displayContent('user'),
|
||||
},
|
||||
log: {
|
||||
title: 'Log',
|
||||
width: '80%',
|
||||
display: displayContent('log'),
|
||||
},
|
||||
timestamp: {
|
||||
title: 'Timestamp',
|
||||
width: '10%',
|
||||
display: displayContent('timestamp')
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('#logfile').change(function () {
|
||||
$('#Logs').jtable('load', {
|
||||
logfile: $('#logfile').val(),
|
||||
user: $('#searchlogs-user').val(),
|
||||
entry: $('#searchlogs-entry').val()
|
||||
});
|
||||
});
|
||||
<?php } else { ?>
|
||||
$('#AboutMe').hide();
|
||||
$('#aboutme').click(function () {
|
||||
$('#MasterZones').hide();
|
||||
$('#SlaveZones').hide();
|
||||
$('#AboutMe').show();
|
||||
});
|
||||
$('#zoneadmin').click(function () {
|
||||
$('#AboutMe').hide();
|
||||
$('#MasterZones').show();
|
||||
$('#SlaveZones').show();
|
||||
});
|
||||
|
||||
$('#Users').jtable('load');
|
||||
<?php } ?>
|
||||
$('#MasterZones').jtable('load');
|
||||
$('#SlaveZones').jtable('load');
|
||||
|
|
96
logs.php
96
logs.php
|
@ -1,96 +0,0 @@
|
|||
<?php
|
||||
|
||||
include_once('includes/config.inc.php');
|
||||
include_once('includes/session.inc.php');
|
||||
include_once('includes/misc.inc.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'])) {
|
||||
header('Status: 400');
|
||||
jtable_respond(null, 'error', 'No action given');
|
||||
}
|
||||
|
||||
if ($logging !== TRUE) {
|
||||
jtable_respond(null, 'error', 'Logging is disabled');
|
||||
} else {
|
||||
switch ($_GET['action']) {
|
||||
|
||||
case "list":
|
||||
if(!empty($_POST['logfile'])) {
|
||||
if(preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{6}\.json/',$_POST['logfile']) == 1) {
|
||||
$entries=json_decode(file_get_contents($logsdirectory . "/" . $_POST['logfile']),true);
|
||||
} else {
|
||||
jtable_respond(null, 'error', "Can't find log file");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$entries=getlogs();
|
||||
}
|
||||
|
||||
if(!empty($_POST['user'])) {
|
||||
$entries=array_filter($entries,
|
||||
function ($val) {
|
||||
return(stripos($val['user'], $_POST['user']) !== FALSE);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if(!empty($_POST['entry'])) {
|
||||
$entries=array_filter($entries,
|
||||
function ($val) {
|
||||
return(stripos($val['log'], $_POST['entry']) !== FALSE);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
jtable_respond($entries);
|
||||
break;
|
||||
|
||||
case "export":
|
||||
if(!empty($_GET['logfile'])) {
|
||||
if(preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{6}\.json/',$_GET['logfile']) == 1) {
|
||||
$entries=json_decode(file_get_contents($logsdirectory . "/" . $_GET['logfile']),true);
|
||||
} else {
|
||||
jtable_respond(null, 'error', "Can't find log file");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$entries=getlogs();
|
||||
}
|
||||
|
||||
if(defined('JSON_PRETTY_PRINT')) {
|
||||
print json_encode($entries,JSON_PRETTY_PRINT);
|
||||
} else {
|
||||
print json_encode($entries);
|
||||
}
|
||||
break;
|
||||
|
||||
case "clear":
|
||||
if($allowclearlogs === TRUE) {
|
||||
clearlogs();
|
||||
} else {
|
||||
jtable_respond(null, 'error', 'Invalid action');
|
||||
}
|
||||
break;
|
||||
case "rotate":
|
||||
if($allowrotatelogs === TRUE) {
|
||||
rotatelogs();
|
||||
} else {
|
||||
jtable_respond(null, 'error', 'Invalid action');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
jtable_respond(null, 'error', 'Invalid action');
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
include_once('includes/config.inc.php');
|
||||
include_once('includes/session.inc.php');
|
||||
include_once('includes/misc.inc.php');
|
||||
|
||||
if(php_sapi_name() !== 'cli') {
|
||||
echo "This script is intended to be run from the command line";
|
||||
} else {
|
||||
if($allowrotatelogs === TRUE) {
|
||||
$current_user['username']='<system>';
|
||||
rotatelogs();
|
||||
} else {
|
||||
echo "Rotating logs has been disabled."
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
{
|
||||
"name": "Example Template",
|
||||
"owner": "public",
|
||||
"records": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "NS",
|
||||
"content": "ns1.example.com.",
|
||||
"label": "ns1"
|
||||
},
|
||||
{
|
||||
"name": "",
|
||||
"type": "NS",
|
||||
"content": "ns2.example.com.",
|
||||
"label": "ns2"
|
||||
},
|
||||
{
|
||||
"name": "example-txt",
|
||||
"type": "TXT",
|
||||
"content": "This is an example txt record"
|
||||
},
|
||||
{
|
||||
"name": "localhost",
|
||||
"type": "A",
|
||||
"content": "127.0.0.1"
|
||||
}
|
||||
]
|
||||
}
|
17
users.php
17
users.php
|
@ -64,13 +64,20 @@ case "create":
|
|||
break;
|
||||
|
||||
case "update":
|
||||
$id = isset($_POST['id']) ? intval($_POST['id']) : '';
|
||||
$emailaddress = isset($_POST['emailaddress']) ? $_POST['emailaddress'] : '';
|
||||
$isadmin = isset($_POST['isadmin']) ? $_POST['isadmin'] : '0';
|
||||
$password = isset($_POST['password']) ? $_POST['password'] : '';
|
||||
|
||||
if ($id != '' and update_user($id, $isadmin, $password)) {
|
||||
$result = array('isadmin' => $isadmin);
|
||||
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');
|
||||
|
@ -78,9 +85,7 @@ case "update":
|
|||
break;
|
||||
|
||||
case "delete":
|
||||
$id = isset($_POST['id']) ? intval($_POST['id']) : '';
|
||||
|
||||
if ($id != '' and delete_user($id) !== FALSE) {
|
||||
if (delete_user($_POST['id']) !== FALSE) {
|
||||
jtable_respond(null, 'delete');
|
||||
} else {
|
||||
jtable_respond(null, 'error', 'Could not delete user');
|
||||
|
|
711
zones.php
711
zones.php
|
@ -3,8 +3,6 @@
|
|||
include_once('includes/config.inc.php');
|
||||
include_once('includes/session.inc.php');
|
||||
include_once('includes/misc.inc.php');
|
||||
include_once('includes/class/PdnsApi.php');
|
||||
include_once('includes/class/Zone.php');
|
||||
|
||||
if (!is_csrf_safe()) {
|
||||
header('Status: 403');
|
||||
|
@ -12,18 +10,236 @@ if (!is_csrf_safe()) {
|
|||
jtable_respond(null, 'error', "Authentication required");
|
||||
}
|
||||
|
||||
function api_request($path, $opts = null, $type = null) {
|
||||
global $apiproto, $apisslverify, $apisid, $apiuser, $apipass, $apiip, $apiport, $authmethod, $apipath;
|
||||
|
||||
$quoteus = array('TXT', 'SPF');
|
||||
$url = "$apiproto://$apiip:$apiport${apipath}${path}";
|
||||
|
||||
if ($authmethod == "auto") {
|
||||
$ad = curl_init();
|
||||
|
||||
if ( strcasecmp( $apiproto, 'https' ) == 0 ) {
|
||||
curl_setopt($ad, CURLOPT_SSL_VERIFYPEER, $apisslverify);
|
||||
}
|
||||
|
||||
curl_setopt($ad, CURLOPT_HTTPHEADER, array('X-API-Key: '.$apipass));
|
||||
curl_setopt($ad, CURLOPT_URL, "$apiproto://$apiip:$apiport/servers/localhost/statistics");
|
||||
curl_setopt($ad, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_exec($ad);
|
||||
if (curl_getinfo($ad, CURLINFO_HTTP_CODE) == 401) {
|
||||
$authmethod = 'userpass';
|
||||
} else {
|
||||
$authmethod = 'xapikey';
|
||||
}
|
||||
}
|
||||
|
||||
$headers = array();
|
||||
array_push($headers, 'Accept: application/json');
|
||||
$ch = curl_init();
|
||||
if ($authmethod == "xapikey") {
|
||||
array_push($headers, 'X-API-Key: '.$apipass);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_USERPWD, "$apiuser:$apipass");
|
||||
}
|
||||
if ( strcasecmp( $apiproto, 'https' ) == 0 ) {
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $apisslverify);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
if ($opts) {
|
||||
if (!$type) {
|
||||
$type = 'POST';
|
||||
}
|
||||
$postdata = json_encode($opts);
|
||||
array_push($headers, 'Content-Type: application/json');
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'DELETE':
|
||||
case 'PATCH':
|
||||
case 'PUT':
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $type);
|
||||
break;
|
||||
case 'POST':
|
||||
break;
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
$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 Error $code: ".$json['error']);
|
||||
} elseif ($code < 200 || $code >= 300) {
|
||||
if ($code == 401) {
|
||||
$code = "Authentication failed. Have you configured your authmethod correct?";
|
||||
}
|
||||
jtable_respond(null, 'error', "API Error: $code");
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
|
||||
function zones_api_request($opts = null, $type = 'POST') {
|
||||
global $apisid;
|
||||
|
||||
return api_request("/servers/${apisid}/zones", $opts, $type);
|
||||
}
|
||||
|
||||
function get_all_zones() {
|
||||
return zones_api_request();
|
||||
}
|
||||
|
||||
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 (!check_owner($zone)) {
|
||||
jtable_respond(null, 'error', 'Access denied');
|
||||
}
|
||||
return $zone;
|
||||
}
|
||||
}
|
||||
}
|
||||
header('Status: 404 Not found');
|
||||
jtable_respond(null, 'error', "Zone not found");
|
||||
}
|
||||
|
||||
function get_zone_by_url($zoneurl) {
|
||||
return _get_zone_by_key('url', $zoneurl);
|
||||
}
|
||||
|
||||
function get_zone_by_id($zoneid) {
|
||||
return _get_zone_by_key('id', $zoneid);
|
||||
}
|
||||
|
||||
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) {
|
||||
return ( bool ) ! preg_match( '/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x80-\\xff]/' , $string );
|
||||
}
|
||||
|
||||
function _valid_label($name) {
|
||||
return is_ascii($name) && ( bool ) preg_match("/^([-.a-z0-9_\/\*]+)?.$/i", $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']);
|
||||
$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,
|
||||
'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) {
|
||||
array_push($records, $record);
|
||||
}
|
||||
}
|
||||
|
||||
return $records;
|
||||
}
|
||||
|
||||
function decode_record_id($id) {
|
||||
|
@ -39,6 +255,39 @@ function decode_record_id($id) {
|
|||
return $record;
|
||||
}
|
||||
|
||||
# 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['disabled'] != $exclude['disabled']) {
|
||||
array_push($records, $record);
|
||||
} else {
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
@ -72,48 +321,31 @@ function rrtype_compare($a, $b) {
|
|||
}
|
||||
}
|
||||
|
||||
function record_compare_default($a, $b) {
|
||||
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 = strnatcasecmp($a['content'], $b['content'])) return $cmp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function record_compare_name($a, $b) {
|
||||
return record_compare_default($a, $b);
|
||||
}
|
||||
|
||||
function record_compare_type($a, $b) {
|
||||
if ($cmp = rrtype_compare($a['type'], $b['type'])) return $cmp;
|
||||
if ($cmp = compareName($a['name'], $b['name'])) return $cmp;
|
||||
if ($cmp = strnatcasecmp($a['content'], $b['content'])) return $cmp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function record_compare_content($a, $b) {
|
||||
if ($cmp = strnatcasecmp($a['content'], $b['content'])) return $cmp;
|
||||
if ($cmp = compareName($a['name'], $b['name'])) return $cmp;
|
||||
if ($cmp = rrtype_compare($a['type'], $b['type'])) return $cmp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function add_db_zone($zonename, $accountname) {
|
||||
if (valid_user($accountname) === false) {
|
||||
jtable_respond(null, 'error', "$accountname is not a valid username");
|
||||
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($accountname)) {
|
||||
add_user($accountname);
|
||||
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, $zonename, SQLITE3_TEXT);
|
||||
$q->bindValue(2, $accountname, SQLITE3_TEXT);
|
||||
$q->bindValue(2, $ownername, SQLITE3_TEXT);
|
||||
$q->execute();
|
||||
$db->close();
|
||||
}
|
||||
|
||||
function delete_db_zone($zonename) {
|
||||
|
@ -124,9 +356,10 @@ function delete_db_zone($zonename) {
|
|||
$q = $db->prepare("DELETE FROM zones WHERE zone = ?");
|
||||
$q->bindValue(1, $zonename, SQLITE3_TEXT);
|
||||
$q->execute();
|
||||
$db->close();
|
||||
}
|
||||
|
||||
function get_zone_account($zonename, $default) {
|
||||
function get_zone_owner($zonename, $default) {
|
||||
if (!_valid_label($zonename)) {
|
||||
jtable_respond(null, 'error', "$zonename is not a valid zonename");
|
||||
}
|
||||
|
@ -135,6 +368,7 @@ function get_zone_account($zonename, $default) {
|
|||
$q->bindValue(1, $zonename, SQLITE3_TEXT);
|
||||
$result = $q->execute();
|
||||
$zoneinfo = $result->fetchArray(SQLITE3_ASSOC);
|
||||
$db->close();
|
||||
if (isset($zoneinfo['emailaddress']) && $zoneinfo['emailaddress'] != null ) {
|
||||
return $zoneinfo['emailaddress'];
|
||||
}
|
||||
|
@ -142,18 +376,28 @@ function get_zone_account($zonename, $default) {
|
|||
return $default;
|
||||
}
|
||||
|
||||
function quote_content($content) {
|
||||
# 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)).'"';
|
||||
function get_zone_keys($zone) {
|
||||
$ret = array();
|
||||
foreach (api_request($zone['url'] . "/cryptokeys") as $key) {
|
||||
if (!isset($key['active']))
|
||||
continue;
|
||||
|
||||
$key['dstxt'] = $zone['name'] . ' IN DNSKEY '.$key['dnskey']."\n\n";
|
||||
|
||||
if (isset($key['ds'])) {
|
||||
foreach ($key['ds'] as $ds) {
|
||||
$key['dstxt'] .= $zone['name'] . ' IN DS '.$ds."\n";
|
||||
}
|
||||
unset($key['ds']);
|
||||
}
|
||||
$ret[] = $key;
|
||||
}
|
||||
|
||||
return $content;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function check_account($zone) {
|
||||
return is_adminuser() or ($zone->account === get_sess_user());
|
||||
function check_owner($zone) {
|
||||
return is_adminuser() or ($zone['owner'] === get_sess_user());
|
||||
}
|
||||
|
||||
if (isset($_GET['action'])) {
|
||||
|
@ -162,100 +406,34 @@ if (isset($_GET['action'])) {
|
|||
jtable_respond(null, 'error', 'No action given');
|
||||
}
|
||||
|
||||
try {
|
||||
$api = new PdnsAPI;
|
||||
|
||||
switch ($action) {
|
||||
|
||||
case "list":
|
||||
case "listslaves":
|
||||
$return = Array();
|
||||
$return = array();
|
||||
$q = isset($_POST['domsearch']) ? $_POST['domsearch'] : false;
|
||||
foreach ($api->listzones($q) as $sresult) {
|
||||
$zone = new Zone();
|
||||
$zone->parse($sresult);
|
||||
if ($zone->account == '') {
|
||||
$zone->setAccount(get_zone_account($zone->name, 'admin'));
|
||||
}
|
||||
|
||||
if (!check_account($zone))
|
||||
foreach (get_all_zones() as $zone) {
|
||||
$zone['owner'] = get_zone_owner($zone['name'], 'admin');
|
||||
if (!check_owner($zone))
|
||||
continue;
|
||||
|
||||
if ($action == "listslaves" and $zone->kind == "Slave") {
|
||||
array_push($return, $zone->export());
|
||||
} elseif ($action == "list" and $zone->kind != "Slave") {
|
||||
if ($zone->dnssec) {
|
||||
$zone->setKeyinfo($api->getzonekeys($zone->id));
|
||||
if ($q && !preg_match("/$q/", $zone['name'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($action == "listslaves" and $zone['kind'] == "Slave") {
|
||||
array_push($return, $zone);
|
||||
} elseif ($action == "list" and $zone['kind'] != "Slave") {
|
||||
if ($zone['dnssec']) {
|
||||
$zone['keyinfo'] = get_zone_keys($zone);
|
||||
}
|
||||
array_push($return, $zone->export());
|
||||
array_push($return, $zone);
|
||||
}
|
||||
}
|
||||
usort($return, "zone_compare");
|
||||
jtable_respond($return);
|
||||
break;
|
||||
|
||||
case "listrecords":
|
||||
$zonedata = $api->loadzone($_GET['zoneid']);
|
||||
$zone = new Zone();
|
||||
$zone->parse($zonedata);
|
||||
$records = $zone->rrsets2records();
|
||||
|
||||
if(!empty($_POST['label'])) {
|
||||
$records=array_filter($records,
|
||||
function ($val) {
|
||||
return(stripos($val['name'], $_POST['label']) !== FALSE);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if(!empty($_POST['type'])) {
|
||||
$records=array_filter($records,
|
||||
function ($val) {
|
||||
return($val['type'] == $_POST['type']);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if(!empty($_POST['content'])) {
|
||||
$records=array_filter($records,
|
||||
function ($val) {
|
||||
return(stripos($val['content'], $_POST['content']) !== FALSE);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($_GET['jtSorting'])) {
|
||||
list($scolumn, $sorder) = preg_split("/ /", $_GET['jtSorting']);
|
||||
switch ($scolumn) {
|
||||
case "type":
|
||||
usort($records, "record_compare_type");
|
||||
break;
|
||||
case "content":
|
||||
usort($records, "record_compare_content");
|
||||
break;
|
||||
default:
|
||||
usort($records, "record_compare_name");
|
||||
break;
|
||||
}
|
||||
if ($sorder == "DESC") {
|
||||
$records = array_reverse($records);
|
||||
}
|
||||
} else {
|
||||
usort($records, "record_compare_name");
|
||||
}
|
||||
jtable_respond($records);
|
||||
break;
|
||||
|
||||
case "delete":
|
||||
$zone = $api->loadzone($_POST['id']);
|
||||
$api->deletezone($_POST['id']);
|
||||
|
||||
delete_db_zone($zone['name']);
|
||||
writelog("Deleted zone ".$zone['name']);
|
||||
jtable_respond(null, 'delete');
|
||||
break;
|
||||
|
||||
|
||||
case "create":
|
||||
$zonename = isset($_POST['name']) ? $_POST['name'] : '';
|
||||
$zonekind = isset($_POST['kind']) ? $_POST['kind'] : '';
|
||||
|
@ -271,50 +449,55 @@ case "create":
|
|||
jtable_respond(null, 'error', "Not enough data");
|
||||
}
|
||||
|
||||
$zone = new Zone();
|
||||
$zone->setKind($zonekind);
|
||||
$zone->setName($zonename);
|
||||
$createOptions = array(
|
||||
'name' => $zonename,
|
||||
'kind' => $zonekind,
|
||||
);
|
||||
|
||||
$nameservers = array();
|
||||
foreach($_POST['nameserver'] as $ns) {
|
||||
if (isset($ns) && !empty($ns)) {
|
||||
array_push($nameservers, $ns);
|
||||
}
|
||||
}
|
||||
|
||||
if ($zonekind != "Slave") {
|
||||
if (!isset($_POST['zone']) or isset($_POST['owns'])) {
|
||||
foreach ($_POST['nameserver'] as $ns) {
|
||||
$zone->addNameserver($ns);
|
||||
$createOptions['nameservers'] = $nameservers;
|
||||
if (!isset($_POST['zone'])) {
|
||||
if (0 == count($nameservers)) {
|
||||
jtable_respond(null, 'error', "Require nameservers");
|
||||
}
|
||||
} else {
|
||||
$zone->importData($_POST['zone']);
|
||||
$createOptions['zone'] = $_POST['zone'];
|
||||
}
|
||||
if (isset($defaults['soa_edit_api'])) {
|
||||
$zone->setSoaEditApi($defaults['soa_edit_api'], True);
|
||||
$createOptions['soa_edit_api'] = $defaults['soa_edit_api'];
|
||||
}
|
||||
if (isset($defaults['soa_edit'])) {
|
||||
$zone->setSoaEdit($defaults['soa_edit']);
|
||||
$createOptions['soa_edit'] = $defaults['soa_edit'];
|
||||
}
|
||||
} else { // Slave
|
||||
if (isset($_POST['masters'])) {
|
||||
foreach (preg_split('/[,;\s]+/', $_POST['masters'], null, PREG_SPLIT_NO_EMPTY) as $master) {
|
||||
$zone->addMaster($master);
|
||||
}
|
||||
$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");
|
||||
}
|
||||
}
|
||||
|
||||
// only admin user and original account can "recreate" zones that are already
|
||||
// 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_account($zonename, get_sess_user())) {
|
||||
if (!is_adminuser() && get_sess_user() !== get_zone_owner($zonename, get_sess_user())) {
|
||||
jtable_respond(null, 'error', 'Zone already owned by someone else');
|
||||
}
|
||||
|
||||
$api->savezone($zone->export());
|
||||
$zone = zones_api_request($createOptions);
|
||||
$zonename = $zone['name'];
|
||||
|
||||
$zone = new Zone();
|
||||
$zone->parse($api->loadzone($zonename));
|
||||
$zonename = $zone->name;
|
||||
|
||||
if (is_adminuser() && isset($_POST['account'])) {
|
||||
add_db_zone($zonename, $_POST['account']);
|
||||
$zone->setAccount($_POST['account']);
|
||||
if (is_adminuser() && isset($_POST['owner'])) {
|
||||
add_db_zone($zonename, $_POST['owner']);
|
||||
} else {
|
||||
add_db_zone($zonename, get_sess_user());
|
||||
$zone->setAccount(get_sess_user());
|
||||
}
|
||||
|
||||
if (isset($_POST['template']) && $_POST['template'] != 'None') {
|
||||
|
@ -322,192 +505,137 @@ case "create":
|
|||
if ($template['name'] !== $_POST['template']) continue;
|
||||
|
||||
foreach ($template['records'] as $record) {
|
||||
$rrset = $zone->getRRSet($record['name'], $record['type']);
|
||||
if ($rrset) {
|
||||
$rrset->delete();
|
||||
if ($record['type'] == 'NS' and array_search($record['content'], $nameservers) !== FALSE) {
|
||||
continue;
|
||||
}
|
||||
if (isset($record['label'])) {
|
||||
$record['name'] = $record['label'];
|
||||
unset($record['label']);
|
||||
}
|
||||
create_record($zone, $record);
|
||||
}
|
||||
$api->savezone($zone->export());
|
||||
|
||||
foreach ($template['records'] as $record) {
|
||||
if ($record['type'] == 'NS') continue;
|
||||
$name = $record['name'] != '' ? join(Array($record['name'],'.',$zonename)) : $zonename;
|
||||
$record['content'] = str_replace("[zonename]", $zonename, $record['content']);
|
||||
$zone->addRecord($name, $record['type'], $record['content']);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$zone = $api->savezone($zone->export());
|
||||
writelog("Created zone ".$zone['name']);
|
||||
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 = new Zone();
|
||||
$zone->parse($api->loadzone($_POST['id']));
|
||||
if ($zone->setSoaEditApi($defaults['soa_edit_api']) != False)
|
||||
writelog("Set SOA-EDIT-API to ".$defaults['soa_edit_api']." for ",$zone->name);
|
||||
$zoneaccount = isset($_POST['account']) ? $_POST['account'] : $zone->account;
|
||||
$zone = get_zone_by_id(isset($_POST['id']) ? $_POST['id'] : '');
|
||||
|
||||
if ($zone->account !== $zoneaccount) {
|
||||
$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 account");
|
||||
jtable_respond(null, 'error', "Can't change owner");
|
||||
} else {
|
||||
add_db_zone($zone->name, $zoneaccount);
|
||||
$zone->setAccount($zoneaccount);
|
||||
add_db_zone($zone['name'], $zoneowner);
|
||||
$zone['owner'] = $zoneowner;
|
||||
}
|
||||
}
|
||||
|
||||
$update = false;
|
||||
|
||||
if (isset($_POST['masters'])) {
|
||||
$zone->eraseMasters();
|
||||
foreach(preg_split('/[,;\s]+/', $_POST['masters'], null, PREG_SPLIT_NO_EMPTY) as $master) {
|
||||
$zone->addMaster($master);
|
||||
}
|
||||
$zone['masters'] = preg_split('/[,;\s]+/', $_POST['masters'], null, PREG_SPLIT_NO_EMPTY);
|
||||
$update = true;
|
||||
}
|
||||
|
||||
writelog("Updated zone ".$zone->name);
|
||||
jtable_respond($api->savezone($zone->export()), 'single');
|
||||
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');
|
||||
break;
|
||||
|
||||
case "listrecords":
|
||||
$zone = get_zone_by_url(isset($_GET['zoneurl']) ? $_GET['zoneurl'] : '');
|
||||
|
||||
$a = api_request($zone['url']);
|
||||
$records = $a['records'];
|
||||
foreach ($records as &$record) {
|
||||
$record['id'] = json_encode($record);
|
||||
}
|
||||
unset($record);
|
||||
usort($records, "record_compare");
|
||||
jtable_respond($records);
|
||||
break;
|
||||
|
||||
case "createrecord":
|
||||
$zone = new Zone();
|
||||
$zone->parse($api->loadzone($_GET['zoneid']));
|
||||
if ($zone->setSoaEditApi($defaults['soa_edit_api']) != False)
|
||||
writelog("Set SOA-EDIT-API to ".$defaults['soa_edit_api']." for ",$zone->name);
|
||||
$zone = get_zone_by_url(isset($_GET['zoneurl']) ? $_GET['zoneurl'] : '');
|
||||
$record = create_record($zone, $_POST);
|
||||
|
||||
$name = isset($_POST['name']) ? $_POST['name'] : '';
|
||||
$type = $_POST['type'];
|
||||
$content = $_POST['content'];
|
||||
|
||||
if ('' == $name) {
|
||||
$name = $zone->name;
|
||||
} elseif (string_ends_with($name, '.')) {
|
||||
# "absolute" name, shouldn't append zone[name] - but check.
|
||||
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;
|
||||
} else {
|
||||
$name = $name.'.';
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
if (array_search($type, $quoteus) !== FALSE) {
|
||||
$content = quote_content($content);
|
||||
}
|
||||
|
||||
$record = $zone->addRecord($name, $type, $content, $_POST['disabled'], $_POST['ttl'], $_POST['setptr']);
|
||||
$api->savezone($zone->export());
|
||||
|
||||
writelog("Created record: ".$record['id']);
|
||||
$record['id'] = json_encode($record);
|
||||
jtable_respond($record, 'single');
|
||||
break;
|
||||
|
||||
case "editrecord":
|
||||
$zone = new Zone();
|
||||
$zone->parse($api->loadzone($_GET['zoneid']));
|
||||
if ($zone->setSoaEditApi($defaults['soa_edit_api']) != False)
|
||||
writelog("Set SOA-EDIT-API to ".$defaults['soa_edit_api']." for ",$zone->name);
|
||||
|
||||
$zone = get_zone_by_url(isset($_GET['zoneurl']) ? $_GET['zoneurl'] : '');
|
||||
$old_record = decode_record_id(isset($_POST['id']) ? $_POST['id'] : '');
|
||||
|
||||
$rrset = $zone->getRRSet($old_record['name'], $old_record['type']);
|
||||
$rrset->deleteRecord($old_record['content']);
|
||||
$records = get_records_except($zone, $old_record);
|
||||
|
||||
$content = $_POST['content'];
|
||||
$type = $_POST['type'];
|
||||
if (array_search($type, $quoteus) !== FALSE) {
|
||||
$content = quote_content($content);
|
||||
$record = make_record($zone, $_POST);
|
||||
|
||||
if ($record['name'] !== $old_record['name'] || $record['type'] !== $old_record['type']) {
|
||||
# rename or retype:
|
||||
$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);
|
||||
}
|
||||
|
||||
$zone->addRecord($_POST['name'], $_POST['type'], $content, $_POST['disabled'], $_POST['ttl'], $_POST['setptr']);
|
||||
|
||||
$api->savezone($zone->export());
|
||||
|
||||
$record = $zone->getRecord($_POST['name'], $_POST['type'], $content);
|
||||
writelog("Updated record ".$_POST['id']." to ".$record['id']);
|
||||
$record['id'] = json_encode($record);
|
||||
jtable_respond($record, 'single');
|
||||
break;
|
||||
|
||||
case "deleterecord":
|
||||
$zone = new Zone();
|
||||
$zone->parse($api->loadzone($_GET['zoneid']));
|
||||
if ($zone->setSoaEditApi($defaults['soa_edit_api']) != False)
|
||||
writelog("Set SOA-EDIT-API to ".$defaults['soa_edit_api']." for ",$zone->name);
|
||||
|
||||
$zone = get_zone_by_url(isset($_GET['zoneurl']) ? $_GET['zoneurl'] : '');
|
||||
$old_record = decode_record_id(isset($_POST['id']) ? $_POST['id'] : '');
|
||||
$rrset = $zone->getRRSet($old_record['name'], $old_record['type']);
|
||||
$rrset->deleteRecord($old_record['content']);
|
||||
|
||||
$api->savezone($zone->export());
|
||||
$records = get_records_except($zone, $old_record);
|
||||
|
||||
writelog("Deleted record ".$_POST['id']);
|
||||
update_records($zone, $old_record, $records);
|
||||
jtable_respond(null, 'delete');
|
||||
break;
|
||||
|
||||
case "export":
|
||||
writelog("Exported zone ".$_GET['zoneid']);
|
||||
jtable_respond($api->exportzone($_GET['zoneid']), 'single');
|
||||
break;
|
||||
$zone = $_GET['zone'];
|
||||
$export = api_request("/servers/${apisid}/zones/${zone}/export");
|
||||
|
||||
case "clone":
|
||||
$name = $_POST['destname'];
|
||||
$src = $_POST['sourcename'];
|
||||
|
||||
if (!string_ends_with($name, '.')) {
|
||||
$name = $name.".";
|
||||
}
|
||||
|
||||
if (!_valid_label($name)) {
|
||||
jtable_respond(null, 'error', "Invalid destination zonename");
|
||||
}
|
||||
|
||||
$srczone = new Zone();
|
||||
$srczone->parse($api->loadzone($src));
|
||||
if ($srczone->setSoaEditApi($defaults['soa_edit_api']) != False)
|
||||
writelog("Set SOA-EDIT-API to ".$defaults['soa_edit_api']." for ",$srczone->name);
|
||||
|
||||
$srczone->setId('');
|
||||
$srczone->setName($name);
|
||||
$srczone->setSerial('');
|
||||
$srczone->setKind($_POST['kind']);
|
||||
|
||||
$zone = $api->savezone($srczone->export());
|
||||
|
||||
$srczone->parse($zone);
|
||||
|
||||
foreach ($srczone->rrsets as $rrset) {
|
||||
$newname = $rrset->name;
|
||||
$newname = preg_replace('/'.$src.'$/', $name, $newname);
|
||||
$rrset->setName($newname);
|
||||
}
|
||||
|
||||
if (is_adminuser() && isset($_POST['account'])) {
|
||||
add_db_zone($name, $_POST['account']);
|
||||
$srczone->setAccount($_POST['account']);
|
||||
} else {
|
||||
add_db_zone($name, get_sess_user());
|
||||
$srczone->setAccount(get_sess_user());
|
||||
}
|
||||
|
||||
$zone = $api->savezone($srczone->export());
|
||||
|
||||
writelog("Cloned zone $src into $name");
|
||||
jtable_respond($zone, 'single');
|
||||
jtable_respond($export, 'single');
|
||||
break;
|
||||
|
||||
case "gettemplatenameservers":
|
||||
|
@ -541,26 +669,7 @@ case "getformnameservers":
|
|||
}
|
||||
}
|
||||
break;
|
||||
case "formzonelist":
|
||||
$zones = $api->listzones();
|
||||
usort($zones, "zone_compare");
|
||||
$ret = array();
|
||||
foreach ($zones as $zone) {
|
||||
if (!check_account($zone))
|
||||
continue;
|
||||
if ($zone['kind'] == 'Slave')
|
||||
continue;
|
||||
array_push($ret, array(
|
||||
'DisplayText' => $zone['name'],
|
||||
'Value' => $zone['id']));
|
||||
}
|
||||
jtable_respond($ret, 'options');
|
||||
break;
|
||||
|
||||
default:
|
||||
jtable_respond(null, 'error', 'No such action');
|
||||
break;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
jtable_respond(null, 'error', $e->getMessage());
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue