Fixed FlexObject::update() removing fields which aren't allowed by ACL

This commit is contained in:
Matias Griese
2019-02-25 15:08:33 +02:00
parent 212d7d24ef
commit 434620dea0
7 changed files with 96 additions and 71 deletions

View File

@@ -6,6 +6,7 @@
1. [](#bugfix)
* Fixed `FlexUser` loosing ACL information
* Fixed `FlexUser::find()` breaking when nothing is found
* Fixed `FlexObject::update()` removing fields which aren't allowed by ACL
# v1.6.0-rc.3
## 02/18/2019

View File

@@ -121,15 +121,31 @@ class Blueprint extends BlueprintForm
*
* @param array $data
* @param bool $missingValuesAsNull
* @param bool $keepEmptyValues
* @return array
*/
public function filter(array $data, bool $missingValuesAsNull = false)
public function filter(array $data, bool $missingValuesAsNull = false, bool $keepEmptyValues = false)
{
$this->initInternals();
return $this->blueprintSchema->filter($data, $missingValuesAsNull);
return $this->blueprintSchema->filter($data, $missingValuesAsNull, $keepEmptyValues);
}
/**
* Flatten data by using blueprints.
*
* @param array $data
* @return array
*/
public function flattenData(array $data)
{
$this->initInternals();
return $this->blueprintSchema->flattenData($data);
}
/**
* Return blueprint data schema.
*

View File

@@ -79,11 +79,51 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
*
* @param array $data Incoming data, for example from a form.
* @param bool $missingValuesAsNull Include missing values as nulls.
* @param bool $keepEmptyValues Include empty values.
* @return array
*/
public function filter(array $data, $missingValuesAsNull = false)
public function filter(array $data, $missingValuesAsNull = false, $keepEmptyValues = false)
{
return $this->filterArray($data, $this->nested, $missingValuesAsNull);
return $this->filterArray($data, $this->nested, $missingValuesAsNull, $keepEmptyValues);
}
/**
* Flatten data by using blueprints.
*
* @param array $data Data to be flattened.
* @return array
*/
public function flattenData(array $data)
{
return $this->flattenArray($data, $this->nested, '');
}
/**
* @param array $data
* @param array $rules
* @param string $prefix
* @return array
*/
protected function flattenArray(array $data, array $rules, string $prefix)
{
$array = [];
foreach ($data as $key => $field) {
$val = $rules[$key] ?? $rules['*'] ?? null;
$rule = is_string($val) ? $this->items[$val] : null;
if ($rule || isset($val['*'])) {
// Item has been defined in blueprints.
$array[$prefix.$key] = $field;
} elseif (is_array($field) && is_array($val)) {
// Array has been defined in blueprints.
$array += $this->flattenArray($field, $val, $prefix . $key . '.');
} else {
// Undefined/extra item.
$array[$prefix.$key] = $field;
}
}
return $array;
}
/**
@@ -124,9 +164,10 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
* @param array $data
* @param array $rules
* @param bool $missingValuesAsNull
* @param bool $keepEmptyValues
* @return array
*/
protected function filterArray(array $data, array $rules, $missingValuesAsNull)
protected function filterArray(array $data, array $rules, $missingValuesAsNull, $keepEmptyValues)
{
$results = [];
@@ -138,7 +179,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
$rule = \is_string($val) ? $this->items[$val] : null;
if (empty($rule['validate']['ignore'])) {
$results[$key] = null;
continue;
}
}
}
@@ -152,26 +193,28 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
// Item has been defined in blueprints.
if (!empty($rule['validate']['ignore'])) {
// Skip any data in the ignored field.
unset($results[$key]);
continue;
}
$field = Validation::filter($field, $rule);
} elseif (\is_array($field) && \is_array($val)) {
// Array has been defined in blueprints.
$field = $this->filterArray($field, $val, $missingValuesAsNull);
$field = $this->filterArray($field, $val, $missingValuesAsNull, $keepEmptyValues);
} elseif (isset($rules['validation']) && $rules['validation'] === 'strict') {
$field = null;
// Skip any extra data.
continue;
}
if (null !== $field && (!\is_array($field) || !empty($field))) {
if ($keepEmptyValues || (null !== $field && (!\is_array($field) || !empty($field)))) {
$results[$key] = $field;
}
}
return $results;
return $results ?: null;
}
/**
* @param array $data
* @param array $toggles

View File

@@ -197,13 +197,15 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
}
/**
* @param bool $missingValuesAsNull
* @return $this
* Filter all items by using blueprints.
*/
public function filter(bool $missingValuesAsNull = false)
public function filter()
{
$this->items = $this->blueprints()->filter($this->items, $missingValuesAsNull);
$args = func_get_args();
$missingValuesAsNull = (bool)(array_shift($args) ?: false);
$keepEmptyValues = (bool)(array_shift($args) ?: false);
$this->items = $this->blueprints()->filter($this->items, $missingValuesAsNull, $keepEmptyValues);
return $this;
}

View File

@@ -150,48 +150,6 @@ class User extends FlexObject implements UserInterface, MediaManipulationInterfa
return $this;
}
/**
* @param array $data
* @param array $files
* @return $this
* @throws ValidationException
*/
public function update(array $data, array $files = [])
{
if ($data) {
// Filter updated data.
$this->filterElements($data);
// Merge data to the existing object.
$elements = $this->getElements();
// Validate and filter the incoming data.
$blueprint = $this->getFlexDirectory()->getBlueprint();
// Merge existing object to the data.
$data = $blueprint->mergeData($elements, $data);
// Validate and filter elements and throw an error if any issues were found.
$blueprint->validate($data + ['storage_key' => $this->getStorageKey(), 'timestamp' => $this->getTimestamp()]);
$data = $blueprint->filter($data);
// Make sure that we add missing (filtered by ACL) elements back.
$data = $blueprint->mergeData($elements, $data);
// Store the changes
$this->_changes = Utils::arrayDiffMultidimensional($data, $this->getElements());
// Finally update the object.
$this->setElements($data);
}
if ($files && method_exists($this, 'setUpdatedMedia')) {
$this->setUpdatedMedia($files);
}
return $this;
}
/**
* Get value from a page variable (used mostly for creating edit forms).
*

View File

@@ -255,7 +255,7 @@ class FlexForm implements FlexFormInterface
protected function filterData(\ArrayAccess $data): void
{
if ($data instanceof Data) {
$data->filter(true);
$data->filter(true, true);
}
}
}

View File

@@ -117,27 +117,32 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface
public function update(array $data, array $files = [])
{
if ($data) {
// Filter updated data.
$blueprint = $this->getBlueprint();
// Process updated data through the object filters.
$this->filterElements($data);
// Merge data to the existing object.
// Get currently stored data.
$elements = $this->getElements();
// Validate and filter the incoming data.
$blueprint = $this->getFlexDirectory()->getBlueprint();
// Merge existing object to the data.
$data = $blueprint->mergeData($elements, $data);
// Merge existing object to the test data to be validated.
$test = $blueprint->mergeData($elements, $data);
// Validate and filter elements and throw an error if any issues were found.
$blueprint->validate($data + ['storage_key' => $this->getStorageKey(), 'timestamp' => $this->getTimestamp()]);
$data = $blueprint->filter($data);
// Store the changes
$this->_changes = Utils::arrayDiffMultidimensional($data, $this->getElements());
$blueprint->validate($test + ['storage_key' => $this->getStorageKey(), 'timestamp' => $this->getTimestamp()]);
$data = $blueprint->filter($data, false, true);
// Finally update the object.
$this->setElements($data);
foreach ($blueprint->flattenData($data) as $key => $value) {
if ($value === null) {
$this->unsetNestedProperty($key);
} else {
$this->setNestedProperty($key, $value);
}
}
// Store the changes
$this->_changes = Utils::arrayDiffMultidimensional($this->getElements(), $elements);
}
if ($files && method_exists($this, 'setUpdatedMedia')) {