Search code examples
phpcakephpviewformhelper

CakePHP: replace input field with another form element (such as lablel) based on user role


I have 2 models:

  1. Books
  2. Users


And i have below rules:

  1. normal users can create/edit books of themselves only
  2. admin users can create/edit books for everyone


I created models of Users and Books and define belong to/one to many realtion I used cake bake to create CRUD views and controllers. My edit controller action for Books is like this:

public function edit($id = null) {
        if (!$this->Book->exists($id)) {
            throw new NotFoundException(__('Invalid book'));
        }
        if ($this->request->is(array('post', 'put'))) {
            if ($this->Book->save($this->request->data)) {
                $this->Session->setFlash(__('The book has been saved.'));
                return $this->redirect(array('action' => 'index'));
            } else {
                $this->Session->setFlash(__('The book could not be saved. Please, try again.'));
            }
        } else {
            $options = array('conditions' => array('Book.' . $this->Book->primaryKey => $id));
            $book = $this->Book->find('first', $options);
            if ($this->isUserCanAccessThisRecordActionEdit($book['BookUser']))
                $this->request->data = $book;
            else
                $this->redirectNotAuthorized();
        }
        $bookUsers = $this->Book->BookUser->find('list');
        $this->set(compact('bookUsers'));
    }

when edit.ctp displayed, it displays users as a dropdown like this: enter image description here Problem: upon rules I defined i don't want allow normal user to choose book_user_id and only admins are allowed to have such list, I want normal user can view their names as BookUser in other format of controls such as labels. I have tried to access request data to display user name directly but I could not do that (I have no problem to differentiate between user and admins in my views, but i don't know how to change UI and access related data to display information)
Another bad news: this way all user name/id pairs are passed to view, so a normal user can have them, which is a bad security idea, e.g. here is the option for Users inside my rendered HTML page of edit.ctp:

     <div class="input select required"><label for="BookBookUserId">Book User</label>
<select name="data[Book][book_user_id]" disabled="disabled" id="BookBookUserId">
<option value="3">vhb</option>
<option value="5">vhb2</option>
<option value="6">vhb3</option>
<option value="7">vhb4</option>
<option value="8">vhb5</option>
<option value="9">vhb6</option>
<option value="10">vhb7</option>
<option value="11">vhb8</option>
<option value="12">vhb9</option>
<option value="13">vhb10</option>
<option value="14">vhb11</option>
<option value="15">vhb12</option>
<option value="16">vhb13</option>
<option value="17">vhb14</option>
<option value="18">vhb15</option>
<option value="19">vhb16</option>
<option value="20">vhb17</option>
<option value="21">root</option>
<option value="22">vhb19</option>
<option value="23">vhb20</option>
<option value="24">vhb21</option>
<option value="25">vhb1</option>
</select></div>

Solution

  • I recommend to use distinct models for administrators and users. It is easy to work and maintain.

    I also suggest to keep your user level checks in AppController.

    For your currently code:

    Supposing you are using a field named "is_admin" to check if the user is admin or not:

    In your controller:

    public function edit($id = null) {
            if (!$this->Book->exists($id)) {
                throw new NotFoundException(__('Invalid book'));
            }
    
            $isAdmin = ($this->Auth->user('is_admin') == true) ? true : false;
            $this->set(compact('isAdmin'));
    
            if ($this->request->is(array('post', 'put'))) {
    
                if ($isAdmin != true) {
    
                    $this->request->data['Book']['book_user_id'] = $this->Auth->user('id');
    
                }
    
                if ($this->Book->save($this->request->data)) {
                    $this->Session->setFlash(__('The book has been saved.'));
                    return $this->redirect(array('action' => 'index'));
                } else {
                    $this->Session->setFlash(__('The book could not be saved. Please, try again.'));
                }
            } else {
                $options = array('conditions' => array('Book.' . $this->Book->primaryKey => $id));
                $book = $this->Book->find('first', $options);
                if ($this->isUserCanAccessThisRecordActionEdit($book['BookUser']))
                    $this->request->data = $book;
                else
                    $this->redirectNotAuthorized();
            }
    
            if ($isAdmin == true) {
    
                $bookUsers = $this->Book->BookUser->find('list');
                $this->set(compact('bookUsers'));
    
            }
    
        }
    

    With this you avoid to load unecessary data.

    In your view:

    if ($isAdmin == true) {
    
        $this->Html->input('book_user_id');
    
    } else {
    
        echo AuthComponent::user('name');
    
    }