Encryption/Decryption of Form Fields in CakePHP 3

前端 未结 2 1737
臣服心动
臣服心动 2020-11-29 10:36

I want to have some form-fields encrypted when they are added/edited and decrypted when they are looked up by cake. Here is the code that works for me in v2.7.2:

<         


        
2条回答
  •  执念已碎
    2020-11-29 11:22

    Edit: @npm was right about the virtual properties not working. Now i'm angry at myself for giving a bad answer. serves me right for not checking it before I posted.

    To make it right, I've implemented a version using behaviors to decrypt the fields as they are read, and encrypt them as they are written to the database.

    Note: This code does not currently incorporate any custom finders, so it will not support searching by the encrypted field.

    eg.

    $this->Patient->findByPatientFirstname('bob'); // this will not work
    

    Behavior

    /src/Model/Behavior/EncryptBehavior.php

     'YOUR_KEY_KERE', /* set them in the EntityTable, not here */
            'fields' => []
        ];
    
    
        /**
         * Before save listener.
         * Transparently manages setting the lft and rght fields if the parent field is
         * included in the parameters to be saved.
         *
         * @param \Cake\Event\Event $event The beforeSave event that was fired
         * @param \Cake\ORM\Entity $entity the entity that is going to be saved
         * @return void
         * @throws \RuntimeException if the parent to set for the node is invalid
         */
        public function beforeSave(Event $event, Entity $entity)
        {
    
            $isNew = $entity->isNew();
            $config = $this->config();
    
    
            $values = $entity->extract($config['fields'], true);
            $fields = array_keys($values);
            $securityKey = $config['key'];
    
            foreach($fields as $field){ 
                if( isset($values[$field]) && !empty($values[$field]) ){
                    $entity->set($field, Security::encrypt($values[$field], $securityKey));
                }
            }
        }
    
        /**
         * Callback method that listens to the `beforeFind` event in the bound
         * table. It modifies the passed query
         *
         * @param \Cake\Event\Event $event The beforeFind event that was fired.
         * @param \Cake\ORM\Query $query Query
         * @param \ArrayObject $options The options for the query
         * @return void
         */
        public function beforeFind(Event $event, Query $query, $options)
        {
            $query->formatResults(function ($results){
                return $this->_rowMapper($results);
            }, $query::PREPEND);
        }
    
        /**
         * Modifies the results from a table find in order to merge the decrypted fields
         * into the results.
         *
         * @param \Cake\Datasource\ResultSetInterface $results Results to map.
         * @return \Cake\Collection\Collection
         */
        protected function _rowMapper($results)
        {
            return $results->map(function ($row) {
                if ($row === null) {
                    return $row;
                }
                $hydrated = !is_array($row);
    
                $fields = $this->_config['fields'];
                $key = $this->_config['key'];
                foreach ($fields as $field) {
                    $row[$field] = Security::decrypt($row[$field], $key);
                }
    
                if ($hydrated) {
                    $row->clean();
                }
    
                return $row;
            });
        }
    }
    

    Table

    /src/Model/Table/PatientsTable.php

    table('patients');
            $this->displayField('id');
            $this->primaryKey('id');
    
            // will encrypt these fields automatically
            $this->addBehavior('Encrypt',[
                'key' => Configure::read('Security.key'),
                'fields' => [
                    'patient_surname',
                    'patient_firstname'
                ]
            ]);
    
        }
    }
    

    I feel your pain. the ORM layer in cakephp 3 is radically different from cake2. They split the entity model and the table ORM into two different classes, and afterFind has been removed. I would take a look at using virtual properties. I think it might be suitable for your use case.

    Example below.

    set('patient_surname', Security::encrypt($str, Configure::read('Security.key'));
        }
    
        protected function _setPatientFirstname($str)
        {
            $this->set('patient_firstname', Security::encrypt($str, Configure::read('Security.key'));
        }
    
        protected function _getPatientSurname()
        {
            return Security::decrypt($this->patient_surname, Configure::read('Security.key'));
        }
    
        protected function _getPatientFirstname()
        {
            return Security::decrypt($this->patient_first_name, Configure::read('Security.key'));
        }
    
    }
    

提交回复
热议问题