Skip to content

Commit

Permalink
v1.1 - Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Ente committed Oct 17, 2024
1 parent 008788b commit 68d77d6
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 64 deletions.
2 changes: 1 addition & 1 deletion .env.sample
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
API_URL="dns.domain.tld:5380"
USERNAME="api_user"
PASSWORD="your-password"
TOKEN="mytoken"
TOKEN=""
INCLUDE_INFO=false
USE_POST=true
USE_HTTPS=true
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ Then: `require_once "/path/to/API.dnsserver.ente.php";` & `use Technitium\DNSSer
- `USERNAME`: The username for the user account. (You should create a dedicated one)
- `PASSWORD`: The password for the user account.
- `INCLUDE_INFO`: Returns basic information that might be relevant for the queried request.
- `TOKEN`: Your API token, if already existent. (`[Your Username]` > `[Create API Token]`)
- `TOKEN`: Your API token, if already existent. (`[Your Username]` > `[Create API Token]`). If left empty, the API will use the username and password for authentication to create an API token for you and will write it to your `.env`.
- `USE_POST`: Specify if you want to access the API via POST (`true`) instead of GET (`false`) in default.
- `USE_HTTPS`: Enable (`true`) HTTPS for the API connection.
- `USE_HTTPS`: Enable (`true`) HTTPS for the API connection. If your server does not support HTTPS, the API will simply return `false` to all requests.

## General Usage

Expand Down Expand Up @@ -96,13 +96,20 @@ use Technitium\DNSServer\API;
use Technitium\DNSServer\API\Helper\DDNS;

DDNS(new API(), file_get_contents("/my/config.json"));
DDNS(new API(), file_get_contents("/my/config2.json"));
DDNS(new API("/my/.env"), file_get_contents("/my/config3.json"));
DDNS(new API(__DIR__), file_get_contents("/my/config2.json"));
DDNS(new API(__DIR__ . "/configurations", ".env-custom"), file_get_contents("/my/config3.json"));

```

## Changes

### v1.1: Fixes

- `TOKEN` can now be empty
- Fixed some endpoints not working
- Fixed issues preventing loading different environments
- Exception handling

### v1.0: Initial Release

- initial release supporting each endpoint
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "ente/technitium-dnsserver-php-api",
"type": "library",
"license": "GPL-3.0-only",
"version": "1.0",
"version": "1.1",
"authors": [
{
"name": "Ente",
Expand All @@ -13,7 +13,8 @@

"require": {
"php": ">=8.0",
"vlucas/phpdotenv": "5.6.1"
"vlucas/phpdotenv": "5.6.1",
"mirazmac/dotenvwriter": "^0.4.0"
},
"autoload": {
"files": [
Expand Down
114 changes: 92 additions & 22 deletions src/API.dnsserver.ente.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Technitium\DNSServer\API\Helper\DDNS;
use Technitium\DNSServer\API\Helper\Log;
use \Dotenv\Dotenv;
use MirazMac\DotEnv\Writer;

class API {

Expand All @@ -34,28 +35,54 @@ class API {

private $log;

public function __construct($confPath = null){
private $conf;

private $path;

private $fullPath;

private $env = [];

public function __construct($confPath = null, $name = null){
$this->loader();
$this->loadConf($confPath = null);
$this->loadConf($confPath, $name);
$this->setProtocol();
}

public function setProtocol(){
if($_ENV["USE_HTTPS"] == "true"){
if($this->env["USE_HTTPS"] == "true"){
$this->protocol = "https";
} else {
$this->protocol = "http";
}
}

public function loadConf($path = null){
public function loadConf($path = null, $name = null){
$this->conf = $name ?? ".env";
$this->path = $path ?? $_SERVER["DOCUMENT_ROOT"];
$this->fullPath = $this->path . "/" . $this->conf;
if($path != null){
$env = \Dotenv\Dotenv::createImmutable($path);
$env->safeLoad();
$env = \Dotenv\Dotenv::createUnsafeMutable($this->path, $this->conf);
Log::error_rep("Using .env file: " . $this->path . "/" . $this->conf);
$env->load();
} else {
$env = \Dotenv\Dotenv::createImmutable(dirname(__DIR__, 1));
$env->safeLoad();
$env = \Dotenv\Dotenv::createUnsafeMutable($this->path);
Log::error_rep("Using .env file: " . $this->path . "/.env");
$env->load();
}
$env->required("API_URL");
$env->required("USERNAME");
$env->required("PASSWORD");
$this->env = [
"API_URL" => getenv("API_URL"),
"USERNAME" => getenv("USERNAME"),
"PASSWORD" => getenv("PASSWORD"),
"USE_HTTPS" => getenv("USE_HTTPS"),
"USE_POST" => @getenv("USE_POST"),
"INCLUDE_INFO" => getenv("INCLUDE_INFO"),
"TOKEN" => @getenv("TOKEN")
];

}

public function loader(){
Expand All @@ -75,50 +102,93 @@ public function loader(){
require_once __DIR__ . "/helper/Log.Helper.API.dnsserver.ente.php";
}

public function sendCall($data, $endpoint, $method = "POST"){
public function sendCall($data, $endpoint, $method = "POST", $skip = false){
$c = curl_init();
$endpoint = $this->prepareEndpoint($endpoint);
if($_ENV["USE_POST"]){
if($this->env["USE_POST"]){
$method = "POST";
}
switch($method){
case "POST":
curl_setopt($c, CURLOPT_URL, $endpoint . $this->appendAuth());
curl_setopt($c, CURLOPT_URL, $endpoint . $this->appendAuth($method,$skip));
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
curl_setopt($c, CURLOPT_POST, true);
curl_setopt($c, CURLOPT_POSTFIELDS, $data);
break;
case "GET":
$data = http_build_query($data);
curl_setopt($c, CURLOPT_URL, $endpoint . "?" . $data . $this->appendAuth());
curl_setopt($c, CURLOPT_URL, $endpoint . "?" . $data . $this->appendAuth($method, $skip));
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
break;
}

$response = curl_exec($c);
if(!$response){
return curl_error($c);
try {
$response = curl_exec($c);
if(!$response){
Log::error_rep("Failed to send request: " . curl_error($c));
return ["status" => "error", "error" => curl_error($c)];
}
} catch (\Throwable $e){
Log::error_rep("Failed to send request: " . $e->getMessage());
return ["status" => "error", "error" => $e->getMessage()];
}
curl_close($c);
if($this->checkResponse($response)){
Log::error_rep("Successfully accessed endpoint: " . $endpoint);
return json_decode($response, true);
}
return [
"error" => "An error occurred",
];
}

public function appendAuth($m = "POST"){
if($_ENV["TOKEN"] != ""){
public function appendAuth($m = "POST", $skip = false){
$this->loadConf($this->path, $this->conf);
if($skip){
return "";
}
if(!empty($this->env["TOKEN"])){
switch($m){
case "POST":
return "?token=" . $_ENV["TOKEN"] . "&user=" . $_ENV["USERNAME"] . "&password=" . $_ENV["PASSWORD"];
return "?token=" . @$this->env["TOKEN"];
case "GET":
return "&token=" . $_ENV["TOKEN"] . "&user=" . $_ENV["USERNAME"] . "&password=" . $_ENV["PASSWORD"];
}
return "&token=" . @$this->env["TOKEN"];
}
} else {
$this->getPermanentToken();
$this->loadConf($this->path, $this->conf);
switch($m){
case "POST":
return "?token=" . @$this->env["TOKEN"];
case "GET":
return "&token=" . @$this->env["TOKEN"];
}
}
}

public function getPermanentToken(){
Log::error_rep("Getting permanent token... | .env: " . $this->fullPath);
$response = $this->sendCall([
"user" => $this->env["USERNAME"],
"pass" => $this->env["PASSWORD"],
"tokenName" => "technitium-dnsserver-php-api"
], "user/createToken", "POST", true);
$writer = new Writer($this->fullPath);
try {
$writer
->set("TOKEN", $response["token"], true)
->set("USERNAME", $this->env["USERNAME"], true)
->set("PASSWORD", $this->env["PASSWORD"], true)
->set("USE_HTTPS", $this->env["USE_HTTPS"], true)
->set("API_URL", $this->env["API_URL"], true)
->set("USE_POST", $this->env["USE_POST"], true)
->set("INCLUDE_INFO", $this->env["INCLUDE_INFO"], true)
->write(true);

} catch(\Throwable $e){
Log::error_rep("Unable to write to .env file: " . $this->fullPath); }
return true;
}

public function checkResponse($response){
if(is_null($response)){
return false;
Expand All @@ -132,7 +202,7 @@ public function prepareEndpoint($endpoint){
$endpoints = json_decode(file_get_contents(__DIR__ . "/helper/endpoints.json"));

if(in_array($endpoint, $endpoints)){
return $this->protocol . "://" . $_ENV["API_URL"] . "/api/" . $endpoint;
return $this->protocol . "://" . $this->env["API_URL"] . "/api/" . $endpoint;
} else {
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions src/endpoints/admin/Sessions.admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ public function createToken(string $username, string $tokenName){
}

/**
* `deleteSession()` - Deletes a session token.
* `delete()` - Deletes a session token.
* - `PERMISSIONS`: `Administration:Modify`
* @param string $partialToken The partial token to delete (Can be obtained from `list()`).
* @return bool Returns `true` if the request was successful, `false` otherwise.
*/
public function deleteSession(string $partialToken){
public function delete(string $partialToken){
$response = $this->API->sendCall(["partialToken" => $partialToken], "admin/sessions/delete", "POST");
return $response["status"] == "ok";
}
Expand Down
5 changes: 3 additions & 2 deletions src/endpoints/admin/Users.admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ public function set(string $username, string $displayName = "", string $newUser
* @param string $username The username of the user to delete.
* @return bool Returns `true` if the request was successful, `false` otherwise.
*/
public function deleteUser(string $username){
return $this->API->sendCall(["user" => $username], "admin/users/delete", "POST");
public function delete(string $username){
$response = $this->API->sendCall(["user" => $username], "admin/users/delete", "POST");
return $response["status"] == "ok";
}

/**
Expand Down
61 changes: 36 additions & 25 deletions src/endpoints/users/Users.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ public function __construct($api){
}

public function eloader(){
require_once __DIR__ . "/Profile.users.php";
require_once __DIR__ . "/Session.users.php";
try {
require_once __DIR__ . "/Profile.users.php";
require_once __DIR__ . "/Session.users.php";
} catch (\Throwable $th) {
Helper\Log::error_rep("Failed to load required enpoint classes: " . $th->getMessage());
}
}


Expand All @@ -27,19 +31,11 @@ public function eloader(){
* @return bool Returns `true` if password was changed successfully.
*/
public function changePassword(string $newPassword){
$response = $this->sendCall(["pass" => $newPassword], "user/changePassword");
return $response["status"] == "ok";
}

/**
* `checkForUpdates()` - Check for updates.
* @return array|bool Returns an result array or `false` otherwise.
*/
public function checkForUpdates($data){
$response = $this->sendCall($data, "cache/checkForUpdates");
if($response["status"] == "ok"){
return $response["response"];
} else {
try {
$response = $this->sendCall(["pass" => $newPassword], "user/changePassword");
return $response["status"] == "ok";
} catch (\Throwable $th) {
Helper\Log::error_rep("Failed to change password: " . $th->getMessage());
return false;
}
}
Expand All @@ -49,10 +45,15 @@ public function checkForUpdates($data){
* @return array|bool Returns a API token or `false` otherwise.
*/
public function createToken(string $username, string $password, string $tokenName){
$response = $this->sendCall(["user" => $username, "pass" => $password, "tokenName" => $tokenName], "user/createToken");
if($response["status"] == "ok"){
return $response["response"];
} else {
try {
$response = $this->sendCall(["user" => $username, "pass" => $password, "tokenName" => $tokenName], "user/createToken");
if($response["status"] == "ok"){
return $response["response"];
} else {
return false;
}
} catch (\Throwable $th) {
Helper\Log::error_rep("Failed to create token: " . $th->getMessage());
return false;
}
}
Expand All @@ -62,10 +63,15 @@ public function createToken(string $username, string $password, string $tokenNam
* @return array|bool Returns relevant information and the token.
*/
public function login(string $username, string $password){
$response = $this->sendCall(["user" => $username, "pass" => $password, "includeInfo" => "true"], "user/login");
if($response["status"] == "ok"){
return $response["response"];
} else {
try {
$response = $this->API->sendCall(["user" => $username, "pass" => $password, "includeInfo" => "true"], "user/login");
if($response["status"] == "ok"){
return $response;
} else {
return false;
}
} catch (\Throwable $th) {
Helper\Log::error_rep("Failed to login: " . $th->getMessage(), $th->getTraceAsString());
return false;
}
}
Expand All @@ -75,8 +81,13 @@ public function login(string $username, string $password){
* @return array|bool Returns relevant information and the token.
*/
public function logout(){
$response = $this->sendCall([], "users/logout");
return $response["status"] == "ok";
try {
$response = $this->sendCall([], "users/logout");
return $response["status"] == "ok";
} catch (\Throwable $th) {
Helper\Log::error_rep("Failed to logout: " . $th->getMessage());
return false;
}
}

public function profile(): profile {
Expand Down
7 changes: 6 additions & 1 deletion src/helper/DDNS.Helper.API.dnsserver.ente.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ public function updateRecords(string $configFile) {
}

private function processRecord($record, $domain) {
$allrecords = $this->API->zones()->records()->get($record)["records"];
try {
$allrecords = $this->API->zones()->records()->get($record)["records"];
} catch (\Throwable $th) {
Log::error_rep("Failed to get records for domain {$record}!");
return;
}
$currentIP = $this->getPublicIP();
$recordFound = false;

Expand Down
Loading

0 comments on commit 68d77d6

Please sign in to comment.