748 lines
24 KiB
PHP
748 lines
24 KiB
PHP
<?php
|
|
|
|
namespace api\classes;
|
|
class API
|
|
{
|
|
public $conn;
|
|
|
|
# The user uuid that requested the API
|
|
protected $user_uuid;
|
|
|
|
# $user_type is either an API call (api) or an call from the frontend (frontend)
|
|
protected $user_type;
|
|
|
|
# Either GET POST PUT or DELETE
|
|
public $request_method;
|
|
|
|
protected $content_type;
|
|
|
|
# The original posted data to the API, this data is NOT sanitized and validated, never use this data for queries!
|
|
public $postedData;
|
|
|
|
# The validated and sanitized data can be uses for the API actions
|
|
public $data;
|
|
|
|
# The permission of the user to check if the action is allowed.
|
|
public $permissions;
|
|
|
|
# The return url that the frontend request will forward to after the api call is done. if set to false it will only output
|
|
# the json response with an http code. API calls always respond with json. $return_url can be set to supply the form with an input
|
|
# with the name _return and value of the url to return to.
|
|
public $return_url;
|
|
|
|
# Required fields & optional fields set by the API actions. This is an array like:
|
|
# Example:
|
|
# 'user_uuid' => ['type' => 'string', 'min' => 5, 'max' => 36],
|
|
# 'user_enabled' => ['type' => 'int', 'min' => 0, 'max' => 99],
|
|
# 'user_active' => ['type' => 'enum', 'values' => ['active', 'inactive', 'banned']],
|
|
# 'user_email' => ['type' => 'string', 'format' => 'email'],
|
|
private $requiredFields = [];
|
|
private $optionalFields = [];
|
|
|
|
public function __construct()
|
|
{
|
|
# Setup Database connection
|
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/bin/php/db_connect.php';
|
|
$this->conn = $GLOBALS['conn'];
|
|
|
|
if (!empty($_SESSION['user']['user_uuid'])) {
|
|
$this->InitUserTypeFrontend();
|
|
} else {
|
|
$this->InitUserTypeAPI();
|
|
}
|
|
|
|
$this->return_url = $this->setReturnUrl();
|
|
|
|
# user_uuid will be set if the user is authorized
|
|
if (!$this->user_uuid) {
|
|
$this->apiOutput(401, ['error' => 'Unauthorized']);
|
|
}
|
|
|
|
# Only allow POST, GET, PUT and DELETE
|
|
if (!$this->checkRequestMethod()) {
|
|
$this->apiOutput(405, ['error' => 'Method not allowed']);
|
|
}
|
|
|
|
|
|
if (!$this->checkContentType()) {
|
|
$this->apiOutput(400, ['error' => 'Unsupported Content-Type.']);
|
|
}
|
|
|
|
if ($this->content_type === 'application/json') {
|
|
if (!$this->checkJson()) {
|
|
$this->apiOutput(400, ['error' => 'Invalid JSON format']);
|
|
}
|
|
}
|
|
|
|
// Disable builder input for non-GET requests to prevent potential SQL injection vulnerabilities.
|
|
// Also disable the builder for users with the 'frontend' user type as an extra security measure.
|
|
// The builder should only be active for API users making GET requests.
|
|
// When building a frontend page, you can still programmatically construct a builder array
|
|
// and set it via $_GET like so after the API class creation:
|
|
// $_GET['builder'] = [1 => ['where' => [0 => 'permission_uuid', 1 => $permission_uuid]]];
|
|
if ($this->request_method !== 'GET' || $this->user_type === 'frontend') {
|
|
$this->disableBuilder();
|
|
}
|
|
|
|
# This converts the posted data if needed to an PHP array
|
|
$this->postedData = $this->processPostedData();
|
|
|
|
}
|
|
|
|
private function InitUserTypeFrontend()
|
|
{
|
|
$this->user_uuid = $_SESSION['user']['user_uuid'];
|
|
$this->user_type = 'frontend';
|
|
|
|
# Load the locale for the user, this is used for the return message in the frontend and other globalFunctions.
|
|
include_once $_SERVER['DOCUMENT_ROOT'] . '/bin/php/Functions/globalFunctions.php';
|
|
$locale = getPreferredLocale();
|
|
global $translations;
|
|
$translations = require $_SERVER['DOCUMENT_ROOT'] . "/bin/locales/{$locale}.php";
|
|
}
|
|
|
|
protected function RecursiveDeleteFolder($folderPath): bool
|
|
{
|
|
// Check if the folder exists
|
|
if (!is_dir($folderPath)) {
|
|
$this->apiOutput(500, ['error' => 'directory not found: ' . $folderPath]);
|
|
}
|
|
|
|
// Get all files and folders in the directory
|
|
$items = array_diff(scandir($folderPath), array('.', '..'));
|
|
|
|
// Loop through each item
|
|
foreach ($items as $item) {
|
|
|
|
$itemPath = $folderPath . DIRECTORY_SEPARATOR . $item;
|
|
|
|
if (is_dir($itemPath)) {
|
|
if (!$this->RecursiveDeleteFolder($itemPath)) {
|
|
$this->apiOutput(500, ['error' => "Unable to remove directory: $itemPath"]);
|
|
}
|
|
} else {
|
|
if (!unlink($itemPath)) {
|
|
$this->apiOutput(500, ['error' => "Unable to delete file: $itemPath"]);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Remove the main folder after all contents are gone
|
|
if (!rmdir($folderPath)) {
|
|
$this->apiOutput(500, ['error' => "Unable to remove directory: $folderPath"]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private function InitUserTypeAPI()
|
|
{
|
|
$this->user_type = 'api';
|
|
|
|
$headers = getallheaders();
|
|
$authHeader = $headers['Authorization'] ?? '';
|
|
|
|
if (!preg_match('/^Bearer\s+(.+)$/', $authHeader, $matches)) {
|
|
$this->apiOutput(401, ['error' => 'Unauthorized, missing bearer token.']);
|
|
}
|
|
|
|
$bearerToken = trim($matches[1]);
|
|
|
|
if (!preg_match('/^[a-f0-9\-]{36}\.[a-f0-9]{64}$/i', $bearerToken)) {
|
|
$this->apiOutput(401, ['error' => 'Unauthorized, invalid token format.']);
|
|
}
|
|
|
|
[$tokenId, $tokenSecret] = explode('.', $bearerToken, 2);
|
|
|
|
$this->user_uuid = $this->validateToken($tokenId, $tokenSecret);
|
|
|
|
if ($this->user_uuid === false) {
|
|
$this->apiOutput(401, ['error' => 'Unauthorized, invalid or expired token.']);
|
|
}
|
|
|
|
$api_token_last_used_timestamp = time();
|
|
$stmt = $this->conn->prepare("UPDATE vc_api_tokens SET api_token_last_used_timestamp = ? WHERE api_token_uuid = ?");
|
|
$stmt->bind_param("is", $api_token_last_used_timestamp, $tokenId);
|
|
$stmt->execute();
|
|
}
|
|
|
|
public function validateSingleData($value, $rules)
|
|
{
|
|
if (!$this->validateField($value, $rules)) {
|
|
$this->apiOutput(400, ['error' => "Invalid value: $value"]);
|
|
}
|
|
|
|
return $this->sanitizeData($value, $rules['type']);
|
|
}
|
|
|
|
public function validateData($requiredFields, $optionalFields = [])
|
|
{
|
|
$inputData = $this->postedData;
|
|
|
|
$this->requiredFields = $requiredFields;
|
|
$this->optionalFields = $optionalFields;
|
|
$sanitizedData = [];
|
|
|
|
foreach ($this->requiredFields as $field => $rules) {
|
|
|
|
if (!array_key_exists($field, $inputData)) {
|
|
$this->apiOutput(400, ['error' => "Missing required field: $field"]);
|
|
}
|
|
|
|
$value = $inputData[$field];
|
|
|
|
if (!$this->validateField($value, $rules)) {
|
|
$this->apiOutput(400, ['error' => "Invalid value for $field"]);
|
|
}
|
|
|
|
$sanitizedData[$field] = $this->sanitizeData($value, $rules['type']);
|
|
}
|
|
|
|
|
|
// Check optional fields
|
|
foreach ($this->optionalFields as $field => $rules) {
|
|
if (isset($inputData[$field])) {
|
|
$value = $inputData[$field];
|
|
|
|
if (!$this->validateField($value, $rules)) {
|
|
$this->apiOutput(422, ['error' => "Invalid value for optional field: $field"]);
|
|
}
|
|
|
|
$sanitizedData[$field] = $this->sanitizeData($value, $rules['type']);
|
|
}
|
|
}
|
|
|
|
if (isset($_GET['builder']) && is_array($_GET['builder'])) {
|
|
foreach ($_GET['builder'] as $builder) {
|
|
if (!isset($builder['where']) || count($builder['where']) !== 2) {
|
|
continue; // skip invalid builders
|
|
}
|
|
|
|
$field = $builder['where'][0];
|
|
$value = $builder['where'][1];
|
|
|
|
// Check if the field is allowed (in required or optional)
|
|
$rules = $requiredFields[$field] ?? $optionalFields[$field] ?? null;
|
|
if (!$rules) {
|
|
$this->apiOutput(403, ['error' => "Field not allowed in query: $field"]);
|
|
}
|
|
|
|
// Validate and sanitize
|
|
if (!$this->validateField($value, $rules)) {
|
|
$this->apiOutput(422, ['error' => "Invalid value for builder field: $field"]);
|
|
}
|
|
|
|
$sanitizedData[$field] = $this->sanitizeData($value, $rules['type']);
|
|
}
|
|
}
|
|
|
|
$this->data = $sanitizedData;
|
|
}
|
|
|
|
private function isValidLength($value, $rules)
|
|
{
|
|
$length = strlen($value);
|
|
if (isset($rules['min']) && $length < $rules['min']) return false;
|
|
if (isset($rules['max']) && $length > $rules['max']) return false;
|
|
return true;
|
|
}
|
|
|
|
private function isValidNumberRange($value, $rules)
|
|
{
|
|
if (isset($rules['min']) && $value < $rules['min']) return false;
|
|
if (isset($rules['max']) && $value > $rules['max']) return false;
|
|
return true;
|
|
}
|
|
|
|
private function validateField($value, $rules)
|
|
{
|
|
switch ($rules['type']) {
|
|
case 'string':
|
|
if (!is_string($value)) return false;
|
|
return $this->isValidLength($value, $rules);
|
|
case 'slugify':
|
|
if (!is_string($value) || !preg_match('/^[a-z0-9]+(-[a-z0-9]+)*$/', $value)) return false;
|
|
return $this->isValidLength($value, $rules);
|
|
|
|
case 'boolean':
|
|
if (is_bool($value)) return true;
|
|
|
|
if (is_string($value)) {
|
|
$value = strtolower($value);
|
|
return $value === 'true' || $value === 'false' || $value === '1' || $value === '0';
|
|
}
|
|
|
|
if (is_int($value)) {
|
|
return $value === 1 || $value === 0;
|
|
}
|
|
|
|
return false;
|
|
|
|
case 'email':
|
|
if (!is_string($value)) return false;
|
|
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) return false;
|
|
return $this->isValidLength($value, $rules);
|
|
|
|
case 'password':
|
|
if (!is_string($value)) return false;
|
|
return $this->isValidLength($value, $rules);
|
|
|
|
case 'html':
|
|
if (!is_string($value)) return false;
|
|
return $this->isValidLength($value, $rules);
|
|
|
|
case 'int':
|
|
if (!is_int($value) && !ctype_digit($value)) return false;
|
|
$value = (int)$value;
|
|
return $this->isValidNumberRange($value, $rules);
|
|
|
|
case 'float':
|
|
// Accept floats or numeric strings
|
|
if (!is_float($value) && !is_numeric($value)) {
|
|
return false;
|
|
}
|
|
$value = (float)$value;
|
|
return $this->isValidNumberRange($value, $rules);
|
|
|
|
case 'timestamp':
|
|
if (is_null($value)) return true;
|
|
if (!is_int($value) && !ctype_digit($value)) return false;
|
|
$value = (int)$value;
|
|
if ($value < 0) return false;
|
|
$min = $rules['min'] ?? 1;
|
|
$max = $rules['max'] ?? 4102444800;
|
|
return $value >= $min && $value <= $max;
|
|
|
|
case 'enum':
|
|
if (!isset($rules['values']) || !in_array($value, $rules['values'], true)) return false;
|
|
return true;
|
|
|
|
case 'uuid':
|
|
if (!is_string($value)) return false;
|
|
return preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i', $value);
|
|
|
|
case 'base64':
|
|
if (!is_string($value)) return false;
|
|
return base64_encode(base64_decode($value, true)) === $value;
|
|
|
|
case 'uuid':
|
|
if (!is_string($value)) return false;
|
|
return preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i', $value);
|
|
|
|
case 'json':
|
|
if (!is_string($value)) return false;
|
|
json_decode($value);
|
|
return json_last_error() === JSON_ERROR_NONE;
|
|
|
|
case 'array':
|
|
if (!is_array($value)) return false;
|
|
return $value;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private function sanitizeData($value, $type)
|
|
{
|
|
switch ($type) {
|
|
case 'string':
|
|
case 'enum':
|
|
case 'uuid':
|
|
// Remove HTML tags and encode special characters
|
|
return htmlspecialchars(strip_tags($value), ENT_QUOTES, 'UTF-8');
|
|
|
|
case 'email':
|
|
// Remove illegal characters from email address
|
|
return filter_var($value, FILTER_SANITIZE_EMAIL);
|
|
|
|
case 'password':
|
|
// Passwords may contain special characters; just trim spaces
|
|
return trim($value);
|
|
|
|
case 'html':
|
|
// Allow safe HTML, you can customize allowed tags
|
|
return strip_tags($value, '<b><i><u><strong><em><p><br>');
|
|
|
|
case 'int':
|
|
// Remove anything that's not a number
|
|
return filter_var($value, FILTER_SANITIZE_NUMBER_INT);
|
|
|
|
case 'base64':
|
|
// Only allow base64 valid characters
|
|
return preg_replace('/[^a-zA-Z0-9\/\+=]/', '', $value);
|
|
|
|
case 'boolean':
|
|
if (is_string($value)) {
|
|
$value = strtolower(trim($value));
|
|
return in_array($value, ['true', '1'], true) ? true : false;
|
|
}
|
|
|
|
return (bool)$value;
|
|
|
|
default:
|
|
// Return as-is if unknown type
|
|
return $value;
|
|
}
|
|
|
|
}
|
|
|
|
public function checkPermissions($permission_name, $accessRightsRequired, $returnBoolean = false)
|
|
{
|
|
$accessLevels = [
|
|
'NA' => 0, // No Access
|
|
'RO' => 1, // Read Only
|
|
'RW' => 2, // Read Write
|
|
];
|
|
$query = "SELECT
|
|
vc_permissions.permission_name,
|
|
vc_user_group_permissions_portal.permission_value
|
|
FROM vc_user_group_permissions_portal
|
|
INNER JOIN vc_permissions ON vc_user_group_permissions_portal.permission_uuid =vc_permissions.permission_uuid
|
|
INNER JOIN vc_users ON vc_user_group_permissions_portal.user_group_uuid = vc_users.user_group_uuid
|
|
WHERE user_uuid = ? AND permission_name = ?";
|
|
|
|
|
|
$stmt = $this->conn->prepare($query);
|
|
$stmt->bind_param("ss", $this->user_uuid, $permission_name);
|
|
$stmt->execute();
|
|
$result = $stmt->get_result()->fetch_assoc();
|
|
|
|
if (!$result) {
|
|
if ($returnBoolean) {
|
|
return false;
|
|
}
|
|
$this->apiOutput(500, ['error' => 'Did not find permission required']);
|
|
}
|
|
|
|
$userAccess = $result['permission_value'];
|
|
|
|
if (!isset($accessLevels[$userAccess]) || !isset($accessLevels[$accessRightsRequired])) {
|
|
if ($returnBoolean) {
|
|
return false;
|
|
}
|
|
$this->apiOutput(500, ['error' => 'Server error.']);
|
|
}
|
|
|
|
// Compare user's access level with the required access level
|
|
if ($accessLevels[$userAccess] < $accessLevels[$accessRightsRequired]) {
|
|
if ($returnBoolean) {
|
|
return false;
|
|
}
|
|
$this->apiOutput(403, ['error' => 'Permission denied. You do not have the required access level.']);
|
|
}
|
|
|
|
if ($returnBoolean) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
protected function setReturnUrl()
|
|
{
|
|
if ($this->user_type !== 'frontend') {
|
|
return false;
|
|
}
|
|
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
|
|
|
if ($method === 'POST' && isset($_POST['_return'])) {
|
|
return $_POST['_return'];
|
|
}
|
|
|
|
if ($method === 'PUT') {
|
|
parse_str(file_get_contents("php://input"), $putData);
|
|
if (isset($putData['_return'])) {
|
|
return $putData['_return'];
|
|
}
|
|
}
|
|
if ($method === 'GET') {
|
|
return false;
|
|
}
|
|
|
|
return $_SERVER['HTTP_REFERER'];
|
|
}
|
|
|
|
protected function checkRequestMethod()
|
|
{
|
|
$allowedMethods = ['GET', 'POST', 'PUT', 'DELETE'];
|
|
$method = $_SERVER['REQUEST_METHOD'] ?? '';
|
|
|
|
if (!in_array($method, $allowedMethods)) {
|
|
return false;
|
|
}
|
|
|
|
# Since browser doesnt allow DELETE or PUTs from the frontend forms (apart from some javascript/ajax fuckery)
|
|
# we need to check the _method POST value.
|
|
if ($this->user_type === 'frontend' && $method === 'POST' && isset($_POST['_method'])) {
|
|
$overrideMethod = strtoupper($_POST['_method']);
|
|
|
|
if (in_array($overrideMethod, ['PUT', 'DELETE'])) {
|
|
$this->request_method = $overrideMethod;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
$this->request_method = $method;
|
|
return true;
|
|
}
|
|
|
|
protected function checkJson()
|
|
{
|
|
$rawInput = file_get_contents('php://input');
|
|
if (empty($rawInput)) {
|
|
return false;
|
|
}
|
|
|
|
json_decode($rawInput, true);
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected function processPostedData()
|
|
{
|
|
if ($this->user_type === 'api') {
|
|
return json_decode(file_get_contents("php://input"), true);
|
|
}
|
|
|
|
switch ($this->request_method) {
|
|
case 'GET':
|
|
return $_GET;
|
|
case 'POST':
|
|
return $_POST;
|
|
case 'PUT':
|
|
case 'DELETE':
|
|
# When an image is uploaded from the front end the data needs to be specified its in $_POST and not $_FILES
|
|
if ($this->content_type === 'multipart/form-data') {
|
|
return $_POST;
|
|
} else {
|
|
parse_str(file_get_contents("php://input"), $data);
|
|
return $data;
|
|
}
|
|
|
|
default:
|
|
return [];
|
|
}
|
|
}
|
|
|
|
protected function validateToken(string $tokenId, string $tokenSecret)
|
|
{
|
|
$stmt = $this->conn->prepare("SELECT user_uuid, api_token FROM vc_api_tokens WHERE api_token_uuid = ? AND api_token_expiration_timestamp > UNIX_TIMESTAMP()");
|
|
$stmt->bind_param("s", $tokenId);
|
|
$stmt->execute();
|
|
|
|
$row = $stmt->get_result()->fetch_assoc();
|
|
|
|
if (!$row) {
|
|
return false;
|
|
}
|
|
|
|
if (!password_verify($tokenSecret, $row['api_token'])) {
|
|
return false;
|
|
}
|
|
|
|
return $row['user_uuid'];
|
|
}
|
|
|
|
|
|
protected function checkContentType()
|
|
{
|
|
# api will need to post with an application/json type Content type.
|
|
# frontend will post with application/x-www-form-urlencoded content type but also is capable of application/json
|
|
# frontend can also post multipart/form-data
|
|
# GET requests dont have an content type
|
|
|
|
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
|
|
|
|
if ($this->request_method === 'GET') {
|
|
$this->content_type = '';
|
|
return true;
|
|
}
|
|
|
|
if ($this->user_type === 'api') {
|
|
$this->content_type = 'application/json';
|
|
return true;
|
|
}
|
|
|
|
if (strpos($contentType, 'application/json') !== false) {
|
|
$this->content_type = 'application/json';
|
|
return true;
|
|
}
|
|
|
|
if (strpos($contentType, 'application/x-www-form-urlencoded') !== false) {
|
|
$this->content_type = 'application/x-www-form-urlencoded';
|
|
return true;
|
|
}
|
|
|
|
if (strpos($contentType, 'multipart/form-data') !== false) {
|
|
$this->content_type = 'multipart/form-data';
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function getUserUuid()
|
|
{
|
|
return $this->user_uuid;
|
|
}
|
|
|
|
public function apiOutput($code = 200, $data = [], $frontendMessage = false)
|
|
{
|
|
if ($this->user_type === 'api') {
|
|
http_response_code($code);
|
|
header('Content-Type: application/json');
|
|
if ($code === 200) {
|
|
echo json_encode(reset($data));
|
|
} else {
|
|
echo json_encode($data);
|
|
}
|
|
|
|
exit;
|
|
}
|
|
|
|
|
|
if ($this->user_type === 'frontend') {
|
|
if (in_array($this->request_method, ['POST', 'PUT', 'DELETE'])) {
|
|
http_response_code($code);
|
|
|
|
if ($this->return_url) { # sometimes the PUT doesnt need an return or response set (Think of js actions to api from frontend)
|
|
$_SESSION['response'] = json_encode($data);
|
|
|
|
# When a request is successfull the api will recieve the data, the frontend needs a friendly message
|
|
if ($frontendMessage) {
|
|
$_SESSION['response'] = json_encode([key($data) => __($frontendMessage)]);
|
|
}
|
|
|
|
header('Location: ' . $this->return_url);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
header('Content-Type: application/json');
|
|
echo json_encode($data);
|
|
exit;
|
|
}
|
|
|
|
exit;
|
|
}
|
|
|
|
public function prepareStatement($query)
|
|
{
|
|
// Enable MySQLi to throw exceptions on errors
|
|
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
|
|
|
try {
|
|
$stmt = $this->conn->prepare($query);
|
|
|
|
} catch (mysqli_sql_exception $e) {
|
|
// If an error occurs during prepare, catch it and return a proper response
|
|
$this->apiOutput(500, ['error' => 'Database error: ' . $e->getMessage()]);
|
|
return null;
|
|
}
|
|
|
|
return $stmt;
|
|
|
|
}
|
|
|
|
public function executeStatement($stmt)
|
|
{
|
|
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
|
|
|
try {
|
|
$stmt->execute();
|
|
return true;
|
|
} catch (mysqli_sql_exception $e) {
|
|
if ($e->getCode() === 1451) {
|
|
$this->apiOutput(409, ['error' => 'Cannot delete record: dependent data exists.']);
|
|
} else {
|
|
$this->apiOutput(500, ['error' => 'Database error: ' . $e->getMessage()]);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public function isSuperuser()
|
|
{
|
|
$query = "SELECT * FROM vc_users WHERE vc_users.user_uuid = ?";
|
|
$stmt = $this->prepareStatement($query);
|
|
$stmt->bind_param('s', $this->user_uuid);
|
|
$this->executeStatement($stmt);
|
|
$result = $stmt->get_result();
|
|
$user_data = $result->fetch_assoc();
|
|
if ($user_data['user_email'] == 'superuser') {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected function buildDynamicQuery(string $tableName): array
|
|
{
|
|
$baseQuery = "SELECT * FROM " . $tableName;
|
|
$whereClauses = [];
|
|
$types = '';
|
|
$values = [];
|
|
|
|
if (!isset($_GET['builder']) || !is_array($_GET['builder'])) {
|
|
return [$baseQuery, $types, $values];
|
|
}
|
|
|
|
foreach ($_GET['builder'] as $builder) {
|
|
if (!isset($builder['where']) || !is_array($builder['where']) || count($builder['where']) !== 2) {
|
|
continue;
|
|
}
|
|
|
|
$column = $builder['where'][0];
|
|
$value = $builder['where'][1];
|
|
|
|
$whereClauses[] = "$column = ?";
|
|
$types .= 's';
|
|
$values[] = $value;
|
|
}
|
|
|
|
if (!empty($whereClauses)) {
|
|
$baseQuery .= " WHERE " . implode(" AND ", $whereClauses);
|
|
}
|
|
|
|
return [$baseQuery, $types, $values];
|
|
}
|
|
|
|
protected function generalGetFunction($query, $types, $params, $returnBoolean, $itemName)
|
|
{
|
|
$stmt = $this->prepareStatement($query);
|
|
|
|
if (!empty($params)) {
|
|
$stmt->bind_param($types, ...$params);
|
|
}
|
|
|
|
$this->executeStatement($stmt);
|
|
$result = $stmt->get_result();
|
|
|
|
if ($result->num_rows === 0) {
|
|
if (!$returnBoolean) {
|
|
$this->apiOutput(404, ['error' => $itemName . ' not found.']);
|
|
}
|
|
}
|
|
|
|
$tokens = [];
|
|
|
|
while ($row = $result->fetch_assoc()) {
|
|
$tokens[] = $row;
|
|
}
|
|
|
|
return $tokens;
|
|
}
|
|
|
|
public function disableBuilder(): void
|
|
{
|
|
if (isset($_GET['builder'])) {
|
|
unset($_GET['builder']);
|
|
}
|
|
}
|
|
|
|
|
|
} |