Files
zoneminder/web/api/app/Controller/SnapshotsController.php
T
Nic Boet 959b47f67d fix: check the group and event links an API save will write
Groups, Snapshots and Tags add/edit checked the ids from MonitorIds or
EventIds when the request had them, but Model::save() links the HABTM
entry (Monitor, Event) and ignores those fields on edit. A Groups=Edit
user could send {"Group":{"MonitorIds":"<current members>"},"Monitor":[]}:
the membership check saw no monitor moved, the save emptied the group and
lifted the Groups_Permissions deny on its monitors. On add, an empty
MonitorIds or EventIds next to a Monitor/Event list let monitors the user
may not edit into a new group, and events the user may not view into a
snapshot.

requestAssociatedIds() now reads the list the way save() does and
rewrites the request's entry to that plain list of ids, so a row given as
a join record links only its MonitorId/EventId. add() copies MonitorIds
or EventIds into the entry before the check, as it did before the save.

Test: tests/php/test_api_associated_ids.php
(cherry picked from commit 6bf015530e)
2026-10-31 18:45:52 -04:00

223 lines
6.5 KiB
PHP

<?php
App::uses('AppController', 'Controller');
/**
* Snapshots Controller
*
* @property Snapshot $Snapshot
* @property PaginatorComponent $Paginator
*/
class SnapshotsController extends AppController {
/**
* Components
*
* @var array
*/
public $components = array('Paginator', 'RequestHandler');
public function beforeFilter() {
parent::beforeFilter();
global $user;
# We already tested for auth in appController, so we just need to test for specific permission
$canView = (!$user) || ($user->Snapshots() != 'None');
if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
}
/**
* index method
*
* @return void
*/
public function index() {
$this->Snapshot->recursive = 1;
if ( $this->request->params['named'] ) {
$this->FilterComponent = $this->Components->load('Filter');
$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
} else {
$conditions = array();
}
$find_array = array(
'conditions' => &$conditions,
'contain' => array('Event'),
'joins' => array(
array(
'table' => 'Snapshots_Events',
'type' => 'left',
'conditions' => array(
'Snapshots_Events.SnapshotId = Snapshot.Id',
),
),
),
'snapshot' => '`Snapshot`.`Id`',
);
$snapshots = array_map(array($this, 'filterContainedEvents'), $this->Snapshot->find('all', $find_array));
$this->set(array(
'snapshots' => $snapshots,
'_serialize' => array('snapshots')
));
}
/**
* view method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function view($id = null) {
$this->Snapshot->recursive = 1;
if (!$this->Snapshot->exists($id)) {
throw new NotFoundException(__('Invalid snapshot'));
}
$options = array('conditions' => array('Snapshot.' . $this->Snapshot->primaryKey => $id));
$snapshot = $this->filterContainedEvents($this->Snapshot->find('first', $options));
$this->set(array(
'snapshot' => $snapshot,
'_serialize' => array('snapshot')
));
}
/**
* add method
*
* @return void
*/
public function add() {
if ( $this->request->is('post') ) {
global $user;
# We already tested for auth in appController,
# so we just need to test for specific permission
$canEdit = (!$user) || ($user->Snapshots() == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
if ( $this->request->data['Snapshot']['EventIds'] and ! isset($this->request->data['Event']) ) {
$this->request->data['Event'] = explode(',', $this->request->data['Snapshot']['EventIds']);
unset($this->request->data['Snapshot']['EventIds']);
}
# A snapshot exposes its events to Snapshots viewers, so only viewable events may be added.
$eventIds = $this->requestAssociatedIds('Snapshot', 'Event');
if ($eventIds) $this->requireEventsView($eventIds);
$this->pinRequestId($this->Snapshot, null);
$this->Snapshot->create();
if ( $this->Snapshot->saveAssociated($this->request->data, array('atomic'=>true)) ) {
return $this->flash(
__('The snapshot has been saved.'),
array('action' => 'index')
);
} else {
ZM\Error("Failed to save Snapshot");
debug($this->Snapshot->invalidFields());
}
} # end if post
$monitors = $this->Snapshot->Event->find('list');
$this->set(compact('monitors'));
} # end add
/**
* edit method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function edit( $id = null ) {
if ( !$this->Snapshot->exists($id) ) {
throw new NotFoundException(__('Invalid snapshot'));
}
if ( $this->request->is(array('post', 'put'))) {
global $user;
# We already tested for auth in appController,
# so we just need to test for specific permission
$canEdit = (!$user) || ($user->Snapshots() == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
$this->pinRequestId($this->Snapshot, $id);
$eventIds = $this->requestAssociatedIds('Snapshot', 'Event');
if ($eventIds) $this->requireEventsView($eventIds);
if ( $this->Snapshot->save($this->request->data) ) {
$message = 'Saved';
} else {
$message = 'Error';
// if there is a validation message, use it
if ( !$this->snapshot->validates() ) {
$message .= ': '.$this->Snapshot->validationErrors;
}
}
} # end if post/put
$snapshot = $this->Snapshot->findById($id);
$this->set(array(
'message' => $message,
'snapshot' => $snapshot,
'_serialize' => array('snapshot')
));
}
/**
* delete method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function delete($id = null) {
$this->Snapshot->id = $id;
if ( !$this->Snapshot->exists() ) {
throw new NotFoundException(__('Invalid snapshot'));
}
$this->request->allowMethod('post', 'delete');
global $user;
# We already tested for auth in appController,
# so we just need to test for specific permission
$canEdit = (!$user) || ($user->Snapshots() == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
if ( $this->Snapshot->delete() ) {
return $this->flash(
__('The snapshot has been deleted.'),
array('action' => 'index')
);
} else {
return $this->flash(
__('The snapshot could not be deleted. Please, try again.'),
array('action' => 'index')
);
}
} // end function delete
// returns event associations
public function associations() {
$this->Snapshot->recursive = -1;
$snapshots = $this->Snapshot->find('all', array(
'contain'=> array(
'Event' => array(
'fields'=>array('Id','Name','MonitorId')
)
)
)
);
$snapshots = array_map(array($this, 'filterContainedEvents'), $snapshots);
$this->set(array(
'snapshots' => $snapshots,
'_serialize' => array('snapshots')
));
} // end associations
} // end class SnapshotController