Cakephp 3 - How to get the currently logged in user in the "Table" class during the checkout process?

I am using CakePhp3 for my website and I have to inject some custom validation logic based on the current user id when I create or modify an object.

Basic case: "Does the user allow the user to change this field to this new value"? If not, I want to raise a validation error (or unauthorized exception).

In cakephp, for which I understand most of the application and business rules should be placed on Models or "ModelsTable" ORMs. But in this class the AuthComponent or the current session is not available.

I don't want to manually call a method on an entity from a controller every time I need to check. I would like to use a validator, something like:

$validator->add('protected_data', 'valid', [
            'rule' => 'canChangeProtectedData',
            'message' => __('You're not able to change this data !'),
            'provider' => 'table',
        ]);

      

Method on ModelTable:

public function canChangeProtectedData($value, array $context)
{
    \Cake\Log\Log::debug("canChangeProtectedData");
    // Find logged user, look at the new value, check if he is authorized to do that, return true/false
    return false;
}

      

I cakephp <3, AuthComponent has a static method "AuthComponent :: user ()" which is no longer available. So how can I do this in CakePhp 3?

Thanks for any answer.

EDIT - adding more details

So here are the details. In case of REST API. I have an object editing function. "Article".

This article has an owner with a foreign key in a column named "user_id" (nothing special here). My users are organized in groups with a group leader. Group leaders can change the owner of an article, but "basics" users cannot do this (but they can edit their own articles). Admin users can edit everything. Therefore, the edit method should be available for any authenticated user, but changing the "user_id" of the object should be allowed and verified depending on the case (if I am an administrator yes, if I am a leader yes, only if the new ID is one of my group, and if I do not have main user).

I can do this check on the controller, but if I want this rule to be checked everywhere in my code where the article was changed (in a different method than the "Edit" method in the article Reference). So to me the model seems like a good place not to be?

+3


source to share


2 answers


Authentication versus authorization

  • Authentication means identifying a user by credentials, which in most cases boil down to "Is the user logged in".
  • Authorization means checking if a user is allowed to perform a certain action.

So don't mix the two.

You don't want to validate the application rules

Taken from the book:

Rules for checking and applying rules

CakePHP's ORM is unique in that it takes a two-layer approach to validation.

The first level is verification. The verification rules are designed to work stateless. They are best used to ensure that the form, data types and data format.

The second level is application rules. Application rules are better for checking the state of your object's objects. For example, validation rules can guarantee that an email address is valid, while an application rule can guarantee that an email address is unique.

What you want to implement is complex application logic and not just simple validation, so the best way to implement this is as an application rule.

I am taking a code snippet from one of my articles that explains a similar case. I had to check the limitation of languages ​​(translations) that can be associated with the model. You can read the entire article here http://florian-kraemer.net/2016/08/complex-application-rules-in-cakephp3/

<?php
namespace App\Model\Rule;

use Cake\Datasource\EntityInterface;
use Cake\ORM\TableRegistry;
use RuntimeException;

class ProfileLanguageLimitRule {

   /**
    * Performs the check
    *
    * @link http://php.net/manual/en/language.oop5.magic.php
    * @param \Cake\Datasource\EntityInterface $entity Entity.
    * @param array $options Options.
    * @return bool
    */
   public function __invoke(EntityInterface $entity, array $options) {
      if (!isset($entity->profile_constraint->amount_of_languages)) {
         if (!isset($entity->profile_constraint_id)) {
            throw new RuntimeException('Profile Constraint ID is missing!');
         }
         $languageLimit = $this->_getConstraintFromDB($entity);
      } else {
         $languageLimit = $entity->profile_constraint->amount_of_languages;
      }

      // Unlimited languages are represented by -1
      if ($languageLimit === -1) {
         return true;
      }

      // -1 Here because the language_id of the profiles table already counts as one language
      // So it always -1 of the constraint value
      $count = count($entity->languages);
      return $count <= ($languageLimit - 1);
   }

   /**
    * Gets the limitation from the ProfileConstraints Table object.
    *
    * @param \Cake\Datasource\EntityInterface $entity Entity.
    * @return int
    */
   protected function _getConstraintFromDB(EntityInterface $entity) {
      $constraintsTable = TableRegistry::get('ProfileConstraints');
      $constraint = $constraintsTable->find()
         ->where([
            'id' => $entity['profile_constraint_id']
         ])
         ->select([
            'amount_of_languages'
         ])
         ->firstOrFail();

      return $constraint->amount_of_languages;
   }

}

      



I think this is pretty self-explanatory. Make sure your user_id field is not "public". Before saving the data right after the fix, add it:

$entity->set('user_id', $this->Auth->user('id'));

      

If you change the snippet above and change profile_constraint_id

to user_id

or whatever else you have in there, this should do the job for you.

What you really need is row / field level authorization

Guess you can use an ACL for this, but I've never really needed a field based ACL. So I can't give you much input on this, but it was (Cake2) and still (Cake3). For Cake3, the ACL stuff has been moved to a plugin . Technically, you can check anything, database fields, strings, whatever.

You can write a behavior that uses an event Model.beforeMarshal

and checks if the user_id (or role or whatever) is present and not empty, and then run validation of all the fields you want for a given user id or user role using the ACL.

Perhaps you could use this PermissionsTable :: check () method or you could write a more dedicated method for checking multiple objects (fields) at the same time. As I said, you will take some time to figure out how best to use ACLs if you go for it.

UX and another cheap solution

At first I would not show the fields at all, the user was not allowed to modify or enter as inputs. If you need to show them, stop, disable form input, or just show it as text. Then use the usual set of validation rules that require the field to be empty (or not present) or an empty list of fields based on your user role. If you don't show the fields, the user will have to align the form and then also do CSRF validation (if used).

+2


source


I don't think you need to check in the table. I just thought about how to do this in the controller.

In my Users / Add app in a controller, for example:



public function add()
{
    $user = $this->Users->newEntity();
    if ($this->request->is('post')) {
        $user = $this->Users->patchEntity($user, $this->request->data);

        //check if user is logged in and is a certain user
        if ($this->request->session()->read('Auth.User.id') === 1) {
            //allow adding/editing role or whatever
            $user->role = $this->request->data('role');
        } else {
            $user->role = 4;//or whatever the correct data is for your problem.
        }
        if ($this->Users->save($user)) {
            $this->Flash->success(__('You have been added.'));
        } else {
            $this->Flash->error(__('You could not be added. Please, try again.'));
        }
    }
    $this->set(compact('user'));
    $this->set('_serialize', ['user']);
}

      

+1


source







All Articles