Files
zoneminder/web/api/app/Controller/ZonesController.php
T
Isaac ConnorandClaude Opus 5.5 ffed77e4d3 fix: pin the record id in Zones, Frames and EventData API writes refs GHSA-f8h6-62c9-x6qr GHSA-993c-fc6p-hpxg
CakePHP's Model::set() takes the record id from a primary key in the
data passed to save(). edit() authorized the id in the URL and then saved
the request body, so Zone[Id]=<other> in the body wrote to that other
zone, past the per-monitor check just added. add() could likewise update
an existing row instead of creating one.

Add AppController::pinRequestId(), which drops the primary key from the
request data and sets the model id, and use it in these edits (pinned to
the URL id) and adds (cleared). Frames and EventData edit() never set the
model id at all, so a body without an Id inserted a new row rather than
updating; pinning fixes that too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 19:38:59 -04:00

216 lines
6.3 KiB
PHP

<?php
App::uses('AppController', 'Controller');
require_once __DIR__ .'/../../../includes/Monitor.php';
/**
* Zones Controller
*
* @property Zone $Zone
*/
class ZonesController extends AppController {
/**
* Components
*
* @var array
*/
public $components = array('RequestHandler');
public function beforeFilter() {
parent::beforeFilter();
global $user;
$canView = (!$user) || ($user->Monitors() != 'None');
if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
}
// Zones belong to a monitor, so changing one needs edit on that monitor, not just
// the global Monitors permission. $monitorId may come from request data.
private function requireMonitorEdit($monitorId) {
$monitor = new ZM\Monitor($monitorId);
if (!$monitor->Id()) {
throw new NotFoundException(__('Invalid monitor'));
}
if (!$monitor->canEdit()) {
throw new UnauthorizedException(__('Insufficient Privileges'));
}
}
// The monitor that the zone being edited or deleted currently belongs to.
private function zoneMonitorId($id) {
$this->Zone->recursive = -1;
$zone = $this->Zone->find('first', array('conditions' => array('Zone.'.$this->Zone->primaryKey => $id)));
return $zone['Zone']['MonitorId'];
}
// The MonitorId given in request data, if any.
private function requestMonitorId() {
$data = $this->request->data;
if (isset($data['Zone']) and is_array($data['Zone'])) $data = $data['Zone'];
return isset($data['MonitorId']) ? $data['MonitorId'] : null;
}
// Find all zones which belong to a MonitorId
public function forMonitor($id = null) {
$this->loadModel('Monitor');
if ( !$this->Monitor->exists($id) ) {
throw new NotFoundException(__('Invalid monitor'));
}
# Monitors=View is coarse. Enforce the per-monitor ACL so zones of a monitor
# the user is denied are not listed by direct monitor Id.
$this->Monitor->recursive = -1;
$monitor = $this->Monitor->find('first', array(
'conditions' => array('Monitor.Id' => $id)
));
$MonitorObj = new ZM\Monitor($monitor['Monitor']);
if ( !$MonitorObj->canView() ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
$this->Zone->recursive = -1;
$zones = $this->Zone->find('all', array(
'conditions' => array('MonitorId' => $id)
));
$this->set(array(
'zones' => $zones,
'_serialize' => array('zones')
));
}
public function index() {
$this->Zone->recursive = -1;
global $user;
$allowedMonitors = ($user and $user->unviewableMonitorIds()) ? $user->viewableMonitorIds() : [];
if (count($allowedMonitors)) {
$mon_options = array('Zones.MonitorId' => $allowedMonitors);
} else {
$mon_options = '';
}
$zones = $this->Zone->find('all',$mon_options);
$this->set(array(
'zones' => $zones,
'_serialize' => array('zones')
));
}
/**
* add method
*
* @return void
*/
public function add() {
if ( !$this->request->is('post') ) {
throw new BadRequestException(__('Invalid method. Should be post'));
return;
}
global $user;
$canEdit = (!$user) || $user->Monitors() == 'Edit' || $user->Monitors() == 'Create';
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
$monitorId = $this->requestMonitorId();
if ($monitorId === null) {
throw new BadRequestException(__('MonitorId is required'));
}
$this->requireMonitorEdit($monitorId);
$this->pinRequestId($this->Zone, null);
$zone = null;
$this->Zone->create();
$zone = $this->Zone->save($this->request->data);
if ( $zone ) {
require_once __DIR__ .'/../../../includes/Monitor.php';
$monitor = new ZM\Monitor($zone['Zone']['MonitorId']);
$monitor->zmcControl('restart');
$message = 'Saved';
//$zone = $this->Zone->find('first', array('conditions' => array( array('Zone.' . $this->Zone->primaryKey => $this->Zone),
} else {
$message = 'Error: ';
// if there is a validation message, use it
if ( !$this->Zone->validates() ) {
$message = $this->Zone->validationErrors;
}
}
$this->set(array(
'message' => $message,
'zone' => $zone,
'_serialize' => array('message','zone')
));
} // end function add()
/**
* edit method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function edit($id = null) {
$this->Zone->id = $id;
if ( !$this->Zone->exists($id) ) {
throw new NotFoundException(__('Invalid zone'));
}
$message = '';
if ( $this->request->is(array('post', 'put')) ) {
global $user;
$canEdit = (!$user) || $user->Monitors() == 'Edit' || $user->Monitors() == 'Create';
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
$this->pinRequestId($this->Zone, $id);
$this->requireMonitorEdit($this->zoneMonitorId($id));
$monitorId = $this->requestMonitorId();
if ($monitorId !== null) $this->requireMonitorEdit($monitorId);
if ( $this->Zone->save($this->request->data) ) {
$message = 'The zone has been saved.';
} else {
$message = 'Error ' . print_r($this->Zone->invalidFields());
}
}
$this->set(array(
'message' => $message,
'_serialize' => array('message')
));
}
/**
* delete method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function delete($id = null) {
$this->Zone->id = $id;
if ( !$this->Zone->exists() ) {
throw new NotFoundException(__('Invalid zone'));
}
$this->request->allowMethod('post', 'delete');
global $user;
$canEdit = (!$user) || $user->Monitors() == 'Edit' || $user->Monitors() == 'Create';
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
$this->requireMonitorEdit($this->zoneMonitorId($id));
if ( $this->Zone->delete() ) {
return $this->flash(__('The zone has been deleted.'), array('action' => 'index'));
} else {
return $this->flash(__('The zone could not be deleted. Please, try again.'), array('action' => 'index'));
}
}
} // end class