HashBundle

The SuluHashBundle adds an easy to use solution for protecting entities and documents from being overridden by accident. This can happen if two users open the same form in the user interface. If the user which opened the form later saves its changes first, the later save will cause the changes to be overridden.

Serialization

The HashSerializeEventSubscriber is the most important part of the HashBundle. The subscriber’s job is to add the hash of the object to the serialized representation being delivered in the response. So the serializer has to be used to deliver the response with the hash value automatically.

Hashing

The HashSerializeEventSubscriber uses the AuditableHasher for hashing documents and entities. This hasher calculates a hash based on the user who modified a document and the time when that change happened. So this only works if the AuditableInterface (for entities) or the AuditableBehavior (for documents) is implemented in the given object.

Validation

This is usually done by the controller itself. The SuluHashBundle comes with a service called sulu_hash.request_hash_checker, which has a checkHash method taking the request, the object and the identifier of an object (since the decoupled component cannot know how to get the identifier of the object).

This method will extract the _hash parameter from the request and compare it with a newly created hash from the passed object, and throw an InvalidHashException in case the hashes do not match. The SuluHashBundle also prepends the configuration of the FOSRestBundle so that it recognizes the Exception and returns a response code with the status code 409 (CONFLICT). It also adds a code of 1103 to the Response, so that certain actions like showing an overlay asking if the changes should be overridden are possible.

The hash checker also checks the value of the query parameter force, which allows to override the object even if it has changed in the mean time.

The following code snippet shows how the hash checker can be included in a controller:

$this->get('sulu_hash.request_hash_checker')
    ->checkHash($request, $object, $object->getId());