diff --git a/CHANGELOG.md b/CHANGELOG.md index 75ac95d3e..9739c8f2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * Fixed error on `bin/gpm plugin uninstall` [#3207](https://github.com/getgrav/grav/issues/3207) * Fixed lowering uppercase characters in usernames when saving from frontend [#2565](https://github.com/getgrav/grav/pull/2565) * Fixed broken min/max validation for field `type: int` + * Fixed save error when editing accounts that have been created with capital letters in their username [#3211](https://github.com/getgrav/grav/issues/3211) # v1.7.5 ## 02/01/2021 diff --git a/system/src/Grav/Common/Flex/Types/Users/Storage/UserFileStorage.php b/system/src/Grav/Common/Flex/Types/Users/Storage/UserFileStorage.php index 4ac561e62..f565c9f17 100644 --- a/system/src/Grav/Common/Flex/Types/Users/Storage/UserFileStorage.php +++ b/system/src/Grav/Common/Flex/Types/Users/Storage/UserFileStorage.php @@ -19,22 +19,6 @@ use Grav\Framework\Flex\Storage\FileStorage; */ class UserFileStorage extends FileStorage { - /** @var bool */ - public $caseSensitive; - - /** - * @param string $key - * @return string - */ - public function normalizeKey(string $key): string - { - if ($this->caseSensitive === true) { - return $key; - } - - return mb_strtolower($key); - } - /** * {@inheritdoc} * @see FlexStorageInterface::getMediaPath() @@ -60,15 +44,4 @@ class UserFileStorage extends FileStorage $row['access'] = $access; } } - - /** - * @param array $options - * @return void - */ - protected function initOptions(array $options): void - { - parent::initOptions($options); - - $this->caseSensitive = $options['case_sensitive'] ?? false; - } } diff --git a/system/src/Grav/Common/Flex/Types/Users/Storage/UserFolderStorage.php b/system/src/Grav/Common/Flex/Types/Users/Storage/UserFolderStorage.php index d1b36a745..7d9924e0d 100644 --- a/system/src/Grav/Common/Flex/Types/Users/Storage/UserFolderStorage.php +++ b/system/src/Grav/Common/Flex/Types/Users/Storage/UserFolderStorage.php @@ -19,22 +19,6 @@ use Grav\Framework\Flex\Storage\FolderStorage; */ class UserFolderStorage extends FolderStorage { - /** @var bool */ - public $caseSensitive; - - /** - * @param string $key - * @return string - */ - public function normalizeKey(string $key): string - { - if ($this->caseSensitive === true) { - return $key; - } - - return mb_strtolower($key); - } - /** * Prepares the row for saving and returns the storage key for the record. * @@ -50,15 +34,4 @@ class UserFolderStorage extends FolderStorage $row['access'] = $access; } } - - /** - * @param array $options - * @return void - */ - protected function initOptions(array $options): void - { - parent::initOptions($options); - - $this->caseSensitive = $options['case_sensitive'] ?? false; - } } diff --git a/system/src/Grav/Common/Flex/Types/Users/UserIndex.php b/system/src/Grav/Common/Flex/Types/Users/UserIndex.php index 9e44236e4..4735c933e 100644 --- a/system/src/Grav/Common/Flex/Types/Users/UserIndex.php +++ b/system/src/Grav/Common/Flex/Types/Users/UserIndex.php @@ -146,11 +146,7 @@ class UserIndex extends FlexIndex */ protected static function filterUsername(string $key, FlexStorageInterface $storage): string { - if (method_exists($storage, 'normalizeKey')) { - return $storage->normalizeKey($key); - } - - return mb_strtolower($key); + return $storage->normalizeKey($key); } /** diff --git a/system/src/Grav/Common/Flex/Types/Users/UserObject.php b/system/src/Grav/Common/Flex/Types/Users/UserObject.php index b589dd5ed..1d803bd6a 100644 --- a/system/src/Grav/Common/Flex/Types/Users/UserObject.php +++ b/system/src/Grav/Common/Flex/Types/Users/UserObject.php @@ -120,11 +120,22 @@ class UserObject extends FlexObject implements UserInterface, Countable // User can only be authenticated via login. unset($elements['authenticated'], $elements['authorized']); - parent::__construct($elements, $key, $directory, $validate); + // Define username if it's not set. + if (!isset($elements['username'])) { + $storageKey = $elements['__META']['storage_key'] ?? null; + if (null !== $storageKey && $key === $directory->getStorage()->normalizeKey($storageKey)) { + $elements['username'] = $storageKey; + } else { + $elements['username'] = $key; + } + } - // Define username and state if they aren't set. - $this->defProperty('username', $key); - $this->defProperty('state', 'enabled'); + // Define state if it isn't set. + if (!isset($elements['state'])) { + $elements['state'] = 'enabled'; + } + + parent::__construct($elements, $key, $directory, $validate); } /** diff --git a/system/src/Grav/Framework/Flex/Storage/AbstractFilesystemStorage.php b/system/src/Grav/Framework/Flex/Storage/AbstractFilesystemStorage.php index 8f20a45e3..97384abce 100644 --- a/system/src/Grav/Framework/Flex/Storage/AbstractFilesystemStorage.php +++ b/system/src/Grav/Framework/Flex/Storage/AbstractFilesystemStorage.php @@ -20,7 +20,6 @@ use Grav\Framework\File\Formatter\MarkdownFormatter; use Grav\Framework\File\Formatter\YamlFormatter; use Grav\Framework\File\Interfaces\FileFormatterInterface; use Grav\Framework\Flex\Interfaces\FlexStorageInterface; -use RocketTheme\Toolbox\File\File; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; use RuntimeException; use function is_array; @@ -37,6 +36,8 @@ abstract class AbstractFilesystemStorage implements FlexStorageInterface protected $keyField = 'storage_key'; /** @var int */ protected $keyLen = 32; + /** @var bool */ + protected $caseSensitive = true; /** * @return bool @@ -98,7 +99,7 @@ abstract class AbstractFilesystemStorage implements FlexStorageInterface public function extractKeysFromRow(array $row): array { return [ - 'key' => $row[$this->keyField] ?? '' + 'key' => $this->normalizeKey($row[$this->keyField] ?? '') ]; } @@ -201,6 +202,19 @@ abstract class AbstractFilesystemStorage implements FlexStorageInterface return substr(hash('sha256', random_bytes($this->keyLen)), 0, $this->keyLen); } + /** + * @param string $key + * @return string + */ + public function normalizeKey(string $key): string + { + if ($this->caseSensitive === true) { + return $key; + } + + return mb_strtolower($key); + } + /** * Checks if a key is valid. * diff --git a/system/src/Grav/Framework/Flex/Storage/FileStorage.php b/system/src/Grav/Framework/Flex/Storage/FileStorage.php index cecaa5319..2dc0757a9 100644 --- a/system/src/Grav/Framework/Flex/Storage/FileStorage.php +++ b/system/src/Grav/Framework/Flex/Storage/FileStorage.php @@ -13,6 +13,7 @@ namespace Grav\Framework\Flex\Storage; use FilesystemIterator; use Grav\Framework\Flex\Interfaces\FlexStorageInterface; +use RuntimeException; use SplFileInfo; /** @@ -50,6 +51,73 @@ class FileStorage extends FolderStorage return $key ? "{$path}/{$key}" : $path; } + /** + * @param string $src + * @param string $dst + * @return bool + */ + public function copyRow(string $src, string $dst): bool + { + if ($this->hasKey($dst)) { + throw new RuntimeException("Cannot copy object: key '{$dst}' is already taken"); + } + + if (!$this->hasKey($src)) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + * @see FlexStorageInterface::renameRow() + */ + public function renameRow(string $src, string $dst): bool + { + if (!$this->hasKey($src)) { + return false; + } + + // Remove old file. + $path = $this->getPathFromKey($src); + $file = $this->getFile($path); + $file->delete(); + + return true; + } + + /** + * @param string $src + * @param string $dst + * @return bool + */ + protected function copyFolder(string $src, string $dst): bool + { + // Nothing to copy. + return true; + } + + /** + * @param string $src + * @param string $dst + * @return bool + */ + protected function moveFolder(string $src, string $dst): bool + { + // Nothing to move. + return true; + } + + /** + * @param string $key + * @return bool + */ + protected function canDeleteFolder(string $key): bool + { + return false; + } + /** * {@inheritdoc} */ diff --git a/system/src/Grav/Framework/Flex/Storage/FolderStorage.php b/system/src/Grav/Framework/Flex/Storage/FolderStorage.php index 514eeea80..acdab7816 100644 --- a/system/src/Grav/Framework/Flex/Storage/FolderStorage.php +++ b/system/src/Grav/Framework/Flex/Storage/FolderStorage.php @@ -242,7 +242,6 @@ class FolderStorage extends AbstractFilesystemStorage return $this->copyFolder($srcPath, $dstPath); } - /** * {@inheritdoc} * @see FlexStorageInterface::renameRow() @@ -360,7 +359,12 @@ class FolderStorage extends AbstractFilesystemStorage */ protected function prepareRow(array &$row): void { - unset($row[$this->keyField]); + if (array_key_exists($this->keyField, $row)) { + $key = $row[$this->keyField]; + if ($key === $this->normalizeKey($key)) { + unset($row[$this->keyField]); + } + } } /** @@ -401,6 +405,8 @@ class FolderStorage extends AbstractFilesystemStorage $key = $this->getNewKey(); } + $key = $this->normalizeKey($key); + // Check if the row already exists and if the key has been changed. $oldKey = $row['__META']['storage_key'] ?? null; if (is_string($oldKey) && $oldKey !== $key) { @@ -679,6 +685,7 @@ class FolderStorage extends AbstractFilesystemStorage $this->indexed = (bool)($options['indexed'] ?? false); $this->keyField = $options['key'] ?? 'storage_key'; $this->keyLen = (int)($options['key_len'] ?? 32); + $this->caseSensitive = (bool)($options['case_sensitive'] ?? true); $variables = ['FOLDER' => '%1$s', 'KEY' => '%2$s', 'KEY:2' => '%3$s', 'FILE' => '%4$s', 'EXT' => '%5$s']; $pattern = Utils::simpleTemplate($pattern, $variables);