169 lines
5.7 KiB
PHP
169 lines
5.7 KiB
PHP
<?php
|
|
|
|
namespace Franzz\Spot;
|
|
|
|
use Franzz\Objects\PhpObject;
|
|
use Franzz\Objects\ToolBox;
|
|
|
|
//TODO Keep only local specificities and move bulk to Franzz\Objects\Controller
|
|
class Controller extends PhpObject
|
|
{
|
|
const MUTATING_ACTIONS = array(
|
|
'add_post',
|
|
'subscribe',
|
|
'unsubscribe',
|
|
'update_project',
|
|
'upload',
|
|
'add_comment',
|
|
'add_position',
|
|
'admin_set',
|
|
'admin_create',
|
|
'admin_delete',
|
|
'build_geojson'
|
|
);
|
|
|
|
private Spot $oSpot;
|
|
private array $asReq;
|
|
private string $sCsrfToken = '';
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct(__CLASS__);
|
|
}
|
|
|
|
private function setReqVal(string $sKey, $oValue, string $sValidation=''): void
|
|
{
|
|
$this->asReq[$sKey] = $this->validateValue($sValidation, $oValue);
|
|
}
|
|
|
|
public function handle($sProcessPage, array $argv = array()): string
|
|
{
|
|
//Start buffering so warnings/notices can be collected
|
|
ob_start();
|
|
|
|
//Parse variables
|
|
$asReq = ToolBox::getRequest($argv);
|
|
$this->asReq = array();
|
|
$sAction = $asReq['a'] ?? '';
|
|
$this->setReqVal('t', $asReq['t'] ?? '');
|
|
$this->setReqVal('name', $asReq['name'] ?? '');
|
|
$this->setReqVal('content', $asReq['content'] ?? '');
|
|
$this->setReqVal('id_project', $asReq['id_project'] ?? 0, 'positiveInt');
|
|
$this->setReqVal('id', $asReq['id'] ?? 0);
|
|
$this->setReqVal('id_entity', $asReq['id'] ?? 0, 'positiveInt');
|
|
$this->setReqVal('field', $asReq['field'] ?? '');
|
|
$this->setReqVal('value', $asReq['value'] ?? '');
|
|
$this->setReqVal('type', $asReq['type'] ?? '');
|
|
$this->setReqVal('email', $asReq['email'] ?? '');
|
|
$this->setReqVal('latitude', $asReq['latitude'] ?? '');
|
|
$this->setReqVal('longitude', $asReq['longitude'] ?? '');
|
|
$this->setReqVal('timestamp', $asReq['timestamp'] ?? 0, 'positiveInt');
|
|
$this->setReqVal('csrf_token', $_SERVER['HTTP_X_CSRF_TOKEN'] ?? ($_POST['csrf_token'] ?? ''));
|
|
|
|
//Create Spot Instance
|
|
$this->oSpot = new Spot($sProcessPage, $this->asReq['t']);
|
|
$this->oSpot->setProjectId($this->asReq['id_project']);
|
|
|
|
//Validate CSRF & dispatch
|
|
if(!$this->validateMutationRequest($sAction)) $sResult = Spot::getJsonResult(false, Spot::UNAUTHORIZED);
|
|
elseif($sAction == '') $sResult = $this->oSpot->getAppMainPage($this->getCsrfToken());
|
|
else $sResult = $this->dispatch($sAction);
|
|
|
|
//Clean errors
|
|
$sDebug = ob_get_clean();
|
|
if($sDebug != '') $this->oSpot->addUncaughtError($sDebug);
|
|
|
|
return $sResult;
|
|
}
|
|
|
|
private function validateMutationRequest(string $sAction): bool
|
|
{
|
|
return
|
|
PHP_SAPI === 'cli'
|
|
||
|
|
!in_array($sAction, self::MUTATING_ACTIONS, true)
|
|
||
|
|
($_SERVER['REQUEST_METHOD'] ?? '') === 'POST' && $this->checkCsrfToken($this->asReq['csrf_token'])
|
|
;
|
|
}
|
|
|
|
private function getCsrfToken(): string
|
|
{
|
|
if($this->sCsrfToken === '') $this->initCsrfToken();
|
|
return $this->sCsrfToken;
|
|
}
|
|
|
|
private function setCsrfToken(): void
|
|
{
|
|
if(empty($_SESSION['csrf_token'])) $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
|
$this->sCsrfToken = $_SESSION['csrf_token'];
|
|
}
|
|
|
|
private function initCsrfToken(): void
|
|
{
|
|
if(PHP_SAPI === 'cli') return;
|
|
|
|
$bCloseSession = false;
|
|
if(session_status() !== PHP_SESSION_ACTIVE) {
|
|
$bSecure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https');
|
|
session_set_cookie_params(array('httponly' => true, 'secure' => $bSecure, 'samesite' => 'Lax'));
|
|
session_start();
|
|
$bCloseSession = true;
|
|
}
|
|
|
|
$this->setCsrfToken();
|
|
if($bCloseSession) session_write_close();
|
|
}
|
|
|
|
private function checkCsrfToken(string $sClientToken): bool
|
|
{
|
|
$sServerToken = $this->getCsrfToken();
|
|
return PHP_SAPI === 'cli' || ($sServerToken !== '' && is_string($sClientToken) && hash_equals($sServerToken, $sClientToken));
|
|
}
|
|
|
|
private function dispatch(string $sAction): string
|
|
{
|
|
return match($sAction) {
|
|
'markers' => $this->oSpot->getMarkers(),
|
|
'last_update' => $this->oSpot->getLastUpdate(),
|
|
'geojson' => $this->oSpot->getProjectGeoJson(),
|
|
'next_feed' => $this->oSpot->getNextFeed($this->asReq['id']),
|
|
'new_feed' => $this->oSpot->getNewFeed($this->asReq['id']),
|
|
'add_post' => $this->oSpot->addPost($this->asReq['name'], $this->asReq['content']),
|
|
'subscribe' => $this->oSpot->subscribe($this->asReq['email'], $this->asReq['name']),
|
|
'unsubscribe' => $this->oSpot->unsubscribe(),
|
|
'unsubscribe_email' => $this->oSpot->unsubscribeFromEmail($this->asReq['id_entity']),
|
|
'update_project' => $this->oSpot->updateProject(),
|
|
default => $this->dispatchAdmin($sAction)
|
|
};
|
|
}
|
|
|
|
private function dispatchAdmin(string $sAction): string
|
|
{
|
|
if(!$this->oSpot->checkUserClearance(User::CLEARANCE_ADMIN)) {
|
|
return Spot::getJsonResult(false, Spot::NOT_FOUND);
|
|
}
|
|
|
|
return match($sAction) {
|
|
'upload' => $this->oSpot->upload(),
|
|
'add_comment' => $this->oSpot->addComment($this->asReq['id_entity'], $this->asReq['content']),
|
|
'add_position' => $this->oSpot->addPosition($this->asReq['latitude'], $this->asReq['longitude'], $this->asReq['timestamp']),
|
|
'admin_get' => $this->oSpot->getAdminSettings(),
|
|
'admin_set' => $this->oSpot->setAdminSettings($this->asReq['type'], $this->asReq['id_entity'], $this->asReq['field'], $this->asReq['value']),
|
|
'admin_create' => $this->oSpot->createAdminSettings($this->asReq['type']),
|
|
'admin_delete' => $this->oSpot->deleteAdminSettings($this->asReq['type'], $this->asReq['id_entity']),
|
|
'sql' => $this->oSpot->getDbBuildScript(),
|
|
'build_geojson' => $this->oSpot->buildGeoJSON($this->asReq['name']),
|
|
default => Spot::getJsonResult(false, Spot::NOT_FOUND)
|
|
};
|
|
}
|
|
|
|
private static function validateValue(string $sValidation, $oValue=0)
|
|
{
|
|
return match($sValidation) {
|
|
'' => $oValue,
|
|
'positiveInt' => filter_var($oValue, FILTER_VALIDATE_INT, array('options' => array('default' => 0, 'min_range' => 0)))
|
|
};
|
|
}
|
|
}
|