diff --git a/.gitignore b/.gitignore index 8c80a71..24297de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1 @@ includes/config.inc.php -nsedit.sublime* -etc -templates.d/*.json diff --git a/Dockerfile b/Dockerfile index 0717e5a..aebdb24 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,28 +1,38 @@ -FROM debian:bookworm +FROM debian:jessie MAINTAINER Yury Evtikhov # # 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: diff --git a/README.md b/README.md index 7469540..622d294 100644 --- a/README.md +++ b/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):///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 =========== diff --git a/css/base.css b/css/base.css index a8cf800..a386d81 100644 --- a/css/base.css +++ b/css/base.css @@ -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%; diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh deleted file mode 100644 index e48f6ee..0000000 --- a/docker-entrypoint.sh +++ /dev/null @@ -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 \ No newline at end of file diff --git a/img/delete.png b/img/delete.png deleted file mode 100644 index f4c24db..0000000 Binary files a/img/delete.png and /dev/null differ diff --git a/img/delete_inverted.png b/img/delete_inverted.png deleted file mode 100644 index 0b05aeb..0000000 Binary files a/img/delete_inverted.png and /dev/null differ diff --git a/includes/class/ApiHandler.php b/includes/class/ApiHandler.php deleted file mode 100644 index 0a53e24..0000000 --- a/includes/class/ApiHandler.php +++ /dev/null @@ -1,135 +0,0 @@ -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(); - } -} - diff --git a/includes/class/PdnsApi.php b/includes/class/PdnsApi.php deleted file mode 100644 index 404a22c..0000000 --- a/includes/class/PdnsApi.php +++ /dev/null @@ -1,128 +0,0 @@ -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; - } - -} - -?> diff --git a/includes/class/Zone.php b/includes/class/Zone.php deleted file mode 100644 index 7f73d81..0000000 --- a/includes/class/Zone.php +++ /dev/null @@ -1,376 +0,0 @@ -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; - } -} - -?> diff --git a/includes/config.inc.php-dist b/includes/config.inc.php-dist index 040ef94..093d9b0 100644 --- a/includes/config.inc.php-dist +++ b/includes/config.inc.php-dist @@ -1,18 +1,23 @@ '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 diff --git a/includes/misc.inc.php b/includes/misc.inc.php index a1662ef..41e407c 100644 --- a/includes/misc.inc.php +++ b/includes/misc.inc.php @@ -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 doc/apiconf.txt'; $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! */ diff --git a/includes/session.inc.php b/includes/session.inc.php index 1900256..0af9f89 100644 --- a/includes/session.inc.php +++ b/includes/session.inc.php @@ -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(); diff --git a/index.php b/index.php index b9cc9be..90da2ee 100644 --- a/index.php +++ b/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) {
- - - -