mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-10-29 17:58:47 -04:00
UsersController::view() and edit() assigned their permission check with `$canX = A or B;`. `=` binds tighter than `or`, so only A was stored and the self check was dropped: with ZM_USER_SELF_EDIT on, a user without System=Edit got 401 on PUT users/<own id>.json. beforeFilter() also refused every user with System=None before view() or edit() ran. Let beforeFilter() through view and edit of the user's own record while ZM_USER_SELF_EDIT is on, use || in both checks, and limit what such a user saves to the fields the web UI allows for self edit (includes/actions/user.php): Password, Language and HomeView. The request data is rebuilt from those three fields, so Id, System, permissions and the rest are never saved; a Language without a lang file is refused as in the UI. Users with System=Edit save as before. Self view follows the web UI too: a user without System permission sees their own record only while ZM_USER_SELF_EDIT is on. fixes #5242 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
222 lines
6.0 KiB
PHP
222 lines
6.0 KiB
PHP
<?php
|
|
App::uses('AppController', 'Controller');
|
|
/**
|
|
* Users Controller
|
|
*
|
|
* @property User $User
|
|
*/
|
|
class UsersController 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->System() != 'None');
|
|
if (!$canView and $this->isSelfEdit()) {
|
|
# view() and edit() check what a user may see and change about themselves
|
|
$canView = true;
|
|
}
|
|
if (!$canView) {
|
|
throw new UnauthorizedException(__('Insufficient Privileges'));
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* index method
|
|
*
|
|
* @return void
|
|
*/
|
|
public function index() {
|
|
$this->User->recursive = 0;
|
|
|
|
global $user;
|
|
# We should actually be able to list our own user, but I'm not bothering at this time.
|
|
if ($user->System() == 'None' ) {
|
|
throw new UnauthorizedException(__('Insufficient Privileges'));
|
|
return;
|
|
}
|
|
$users = $this->Paginator->paginate('User');
|
|
|
|
$this->set(compact('users'));
|
|
}
|
|
|
|
/**
|
|
* view method
|
|
*
|
|
* @throws NotFoundException
|
|
* @param string $id
|
|
* @return void
|
|
*/
|
|
public function view($id = null) {
|
|
$this->User->recursive = 1;
|
|
|
|
global $user;
|
|
# We can view ourselves when ZM_USER_SELF_EDIT allows us to edit ourselves
|
|
$canView = ($user->System() != 'None') || (ZM_USER_SELF_EDIT && ($user->Id() == $id));
|
|
if (!$canView) {
|
|
throw new UnauthorizedException(__('Insufficient Privileges'));
|
|
return;
|
|
}
|
|
|
|
if (!$this->User->exists($id)) {
|
|
throw new NotFoundException(__('Invalid user'));
|
|
}
|
|
|
|
$options = array('conditions' => array('User.' . $this->User->primaryKey => $id));
|
|
$user = $this->User->find('first', $options);
|
|
|
|
$this->set(array(
|
|
'user' => $user,
|
|
'_serialize' => array('user')
|
|
));
|
|
}
|
|
|
|
/**
|
|
* add method
|
|
*
|
|
* @return void
|
|
*/
|
|
public function add() {
|
|
if ($this->request->is('post')) {
|
|
global $user;
|
|
if ($user->System() != 'Edit') {
|
|
throw new UnauthorizedException(__('Insufficient Privileges'));
|
|
return;
|
|
}
|
|
|
|
$this->User->create();
|
|
if ($this->User->save($this->request->data)) {
|
|
$message = 'Saved';
|
|
} else {
|
|
$message = 'Error';
|
|
// if there is a validation message, use it
|
|
if (!$this->User->validates()) {
|
|
$message = $this->User->validationErrors;
|
|
}
|
|
}
|
|
} else {
|
|
$message = 'Add without post data';
|
|
}
|
|
$this->set(array(
|
|
'user' => $this->User,
|
|
'message' => $message,
|
|
'_serialize' => array('message')
|
|
));
|
|
}
|
|
|
|
/**
|
|
* edit method
|
|
*
|
|
* @throws NotFoundException
|
|
* @param string $id
|
|
* @return void
|
|
*/
|
|
public function edit($id = null) {
|
|
$this->User->id = $id;
|
|
|
|
global $user;
|
|
$selfEdit = ($user->System() != 'Edit');
|
|
$canEdit = ($user->System() == 'Edit') || (ZM_USER_SELF_EDIT && ($user->Id() == $id));
|
|
if (!$canEdit) {
|
|
throw new UnauthorizedException(__('Insufficient Privileges'));
|
|
return;
|
|
}
|
|
|
|
if (!$this->User->exists($id)) {
|
|
throw new NotFoundException(__('Invalid user'));
|
|
}
|
|
|
|
if ($this->request->is('post') || $this->request->is('put')) {
|
|
$data = $this->request->data;
|
|
$fieldList = array();
|
|
if ($selfEdit) {
|
|
# Same fields the web UI lets a user change about themselves (includes/actions/user.php)
|
|
$data = $this->selfEditData($data);
|
|
$fieldList = array_keys($data['User']);
|
|
}
|
|
if ($selfEdit and !count($fieldList)) {
|
|
$message = 'Error';
|
|
} else if ( $this->User->save($data, true, $fieldList) ) {
|
|
$message = 'Saved';
|
|
} else {
|
|
$message = 'Error';
|
|
if (!$this->User->validates()) {
|
|
$message = $this->User->validationErrors;
|
|
}
|
|
}
|
|
} else {
|
|
# What is this doing? Resetting the request data? I understand clearing the password field
|
|
# but generally I feel like the request data should be read only
|
|
$this->request->data = $this->User->read(null, $id);
|
|
unset($this->request->data['User']['Password']);
|
|
}
|
|
|
|
$this->set(array(
|
|
'message' => $message,
|
|
'_serialize' => array('message')
|
|
));
|
|
}
|
|
|
|
/**
|
|
* delete method
|
|
*
|
|
* @throws NotFoundException
|
|
* @param string $id
|
|
* @return void
|
|
*/
|
|
public function delete($id = null) {
|
|
$this->User->id = $id;
|
|
|
|
global $user;
|
|
# Can't delete ourselves
|
|
if ( ($user->System() != 'Edit') or ($user->Id() == $id) ) {
|
|
throw new UnauthorizedException(__('Insufficient Privileges'));
|
|
return;
|
|
}
|
|
if ( !$this->User->exists() ) {
|
|
throw new NotFoundException(__('Invalid user'));
|
|
}
|
|
$this->request->allowMethod('post', 'delete');
|
|
if ($this->User->delete()) {
|
|
$message = 'The user has been deleted.';
|
|
} else {
|
|
$message = 'The user could not be deleted. Please, try again.';
|
|
}
|
|
$this->set(array(
|
|
'message' => $message,
|
|
'_serialize' => array('message')
|
|
));
|
|
}
|
|
|
|
# True for view or edit of the logged in user's own record while ZM_USER_SELF_EDIT is on
|
|
private function isSelfEdit() {
|
|
global $user;
|
|
if (!$user or !ZM_USER_SELF_EDIT) return false;
|
|
if (!in_array($this->request->params['action'], array('view', 'edit'))) return false;
|
|
$pass = $this->request->params['pass'];
|
|
return isset($pass[0]) && ($pass[0] == $user->Id());
|
|
}
|
|
|
|
# Keep only Password, Language and HomeView from the request, as the web UI does for self edit
|
|
private function selfEditData($data) {
|
|
$in = (isset($data['User']) and is_array($data['User'])) ? $data['User'] : $data;
|
|
if (!is_array($in)) $in = array();
|
|
$fields = array_intersect_key($in, array('Password'=>'', 'Language'=>'', 'HomeView'=>''));
|
|
if (empty($fields['Password'])) unset($fields['Password']);
|
|
if (!empty($fields['Language']) and
|
|
(!is_string($fields['Language']) or !file_exists(ZM_PATH_WEB.'/lang/'.$fields['Language'].'.php'))) {
|
|
throw new BadRequestException(__('Invalid Language'));
|
|
}
|
|
return array('User' => $fields);
|
|
}
|
|
} # end class UsersController
|