diff --git a/src/Database/Database.php b/src/Database/Database.php index a5af5996e..f012a3462 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -80,6 +80,7 @@ class Database public const MAX_DOUBLE = PHP_FLOAT_MAX; public const MAX_VECTOR_DIMENSIONS = 16000; public const MAX_ARRAY_INDEX_LENGTH = 255; + public const MAX_UID_DEFAULT_LENGTH = 36; // Global SRID for geographic coordinates (WGS84) public const DEFAULT_SRID = 4326; @@ -5151,6 +5152,7 @@ public function updateDocuments( $indexes, $this->adapter->getIdAttributeType(), $this->maxQueryValues, + $this->adapter->getMaxUIDLength(), $this->adapter->getMinDateTime(), $this->adapter->getMaxDateTime(), $this->adapter->getSupportForAttributes() @@ -6710,6 +6712,7 @@ public function deleteDocuments( $indexes, $this->adapter->getIdAttributeType(), $this->maxQueryValues, + $this->adapter->getMaxUIDLength(), $this->adapter->getMinDateTime(), $this->adapter->getMaxDateTime(), $this->adapter->getSupportForAttributes() @@ -6908,6 +6911,7 @@ public function find(string $collection, array $queries = [], string $forPermiss $indexes, $this->adapter->getIdAttributeType(), $this->maxQueryValues, + $this->adapter->getMaxUIDLength(), $this->adapter->getMinDateTime(), $this->adapter->getMaxDateTime(), $this->adapter->getSupportForAttributes() @@ -7139,6 +7143,7 @@ public function count(string $collection, array $queries = [], ?int $max = null) $indexes, $this->adapter->getIdAttributeType(), $this->maxQueryValues, + $this->adapter->getMaxUIDLength(), $this->adapter->getMinDateTime(), $this->adapter->getMaxDateTime(), $this->adapter->getSupportForAttributes() @@ -7204,6 +7209,7 @@ public function sum(string $collection, string $attribute, array $queries = [], $indexes, $this->adapter->getIdAttributeType(), $this->maxQueryValues, + $this->adapter->getMaxUIDLength(), $this->adapter->getMinDateTime(), $this->adapter->getMaxDateTime(), $this->adapter->getSupportForAttributes() diff --git a/src/Database/Validator/Key.php b/src/Database/Validator/Key.php index a8041222c..843444677 100644 --- a/src/Database/Validator/Key.php +++ b/src/Database/Validator/Key.php @@ -2,20 +2,11 @@ namespace Utopia\Database\Validator; +use Utopia\Database\Database; use Utopia\Validator; class Key extends Validator { - protected bool $allowInternal = false; // If true, you keys starting with $ are allowed - - /** - * Maximum length for Key validation - */ - protected int $maxLength; - - /** - * @var string - */ protected string $message; /** @@ -33,10 +24,10 @@ public function getDescription(): string /** * Expression constructor */ - public function __construct(bool $allowInternal = false, int $maxLength = 255) - { - $this->allowInternal = $allowInternal; - $this->maxLength = $maxLength; + public function __construct( + protected readonly bool $allowInternal = false, + protected readonly int $maxLength = Database::MAX_UID_DEFAULT_LENGTH, + ) { $this->message = 'Parameter must contain at most ' . $this->maxLength . ' chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char'; } @@ -46,7 +37,6 @@ public function __construct(bool $allowInternal = false, int $maxLength = 255) * Returns true if valid or false if not. * * @param $value - * * @return bool */ public function isValid($value): bool @@ -59,7 +49,7 @@ public function isValid($value): bool return false; } - // no leading special characters + // No leading special characters $leading = \mb_substr($value, 0, 1); if ($leading === '_' || $leading === '.' || $leading === '-') { return false; @@ -67,7 +57,6 @@ public function isValid($value): bool $isInternal = $leading === '$'; - if ($isInternal && !$this->allowInternal) { return false; } @@ -83,6 +72,7 @@ public function isValid($value): bool if (\preg_match('/[^A-Za-z0-9\_\-\.]/', $value)) { return false; } + // At most maxLength chars if (\mb_strlen($value) > $this->maxLength) { return false; diff --git a/src/Database/Validator/Label.php b/src/Database/Validator/Label.php index 79b9e56c6..cf09be0b1 100644 --- a/src/Database/Validator/Label.php +++ b/src/Database/Validator/Label.php @@ -2,10 +2,14 @@ namespace Utopia\Database\Validator; +use Utopia\Database\Database; + class Label extends Key { - public function __construct(bool $allowInternal = false, int $maxLength = 255) - { + public function __construct( + bool $allowInternal = false, + int $maxLength = Database::MAX_UID_DEFAULT_LENGTH + ) { parent::__construct($allowInternal, $maxLength); $this->message = 'Value must be a valid string between 1 and ' . $this->maxLength . ' chars containing only alphanumeric chars'; } diff --git a/src/Database/Validator/Queries/Documents.php b/src/Database/Validator/Queries/Documents.php index ebdfdf05a..e55852bb8 100644 --- a/src/Database/Validator/Queries/Documents.php +++ b/src/Database/Validator/Queries/Documents.php @@ -2,7 +2,6 @@ namespace Utopia\Database\Validator\Queries; -use Exception; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\IndexedQueries; @@ -16,18 +15,21 @@ class Documents extends IndexedQueries { /** - * Expression constructor - * * @param array $attributes * @param array $indexes * @param string $idAttributeType - * @throws Exception + * @param int $maxValuesCount + * @param \DateTime $minAllowedDate + * @param \DateTime $maxAllowedDate + * @param bool $supportForAttributes + * @throws \Utopia\Database\Exception */ public function __construct( array $attributes, array $indexes, string $idAttributeType, int $maxValuesCount = 5000, + int $maxUIDLength = 36, \DateTime $minAllowedDate = new \DateTime('0000-01-01'), \DateTime $maxAllowedDate = new \DateTime('9999-12-31'), bool $supportForAttributes = true @@ -60,7 +62,7 @@ public function __construct( $validators = [ new Limit(), new Offset(), - new Cursor(), + new Cursor($maxUIDLength), new Filter( $attributes, $idAttributeType, diff --git a/src/Database/Validator/Query/Cursor.php b/src/Database/Validator/Query/Cursor.php index 46020d7b4..58053fe60 100644 --- a/src/Database/Validator/Query/Cursor.php +++ b/src/Database/Validator/Query/Cursor.php @@ -2,12 +2,17 @@ namespace Utopia\Database\Validator\Query; +use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Validator\UID; class Cursor extends Base { + public function __construct(private readonly int $maxLength = Database::MAX_UID_DEFAULT_LENGTH) + { + } + /** * Is valid. * @@ -33,7 +38,7 @@ public function isValid($value): bool $cursor = $cursor->getId(); } - $validator = new UID(); + $validator = new UID($this->maxLength); if ($validator->isValid($cursor)) { return true; } diff --git a/src/Database/Validator/UID.php b/src/Database/Validator/UID.php index 4dab38185..743adbcde 100644 --- a/src/Database/Validator/UID.php +++ b/src/Database/Validator/UID.php @@ -2,12 +2,14 @@ namespace Utopia\Database\Validator; +use Utopia\Database\Database; + class UID extends Key { /** * Expression constructor */ - public function __construct(int $maxLength = 255) + public function __construct(int $maxLength = Database::MAX_UID_DEFAULT_LENGTH) { parent::__construct(false, $maxLength); } diff --git a/tests/unit/Validator/KeyTest.php b/tests/unit/Validator/KeyTest.php index e09ef402e..3c19346d8 100644 --- a/tests/unit/Validator/KeyTest.php +++ b/tests/unit/Validator/KeyTest.php @@ -66,8 +66,8 @@ public function testValues(): void $this->assertEquals(false, $this->object->isValid('as+5dasdasdas')); $this->assertEquals(false, $this->object->isValid('as=5dasdasdas')); - // At most 255 chars - $this->assertEquals(true, $this->object->isValid(str_repeat('a', 255))); + // At most 36 chars + $this->assertEquals(true, $this->object->isValid(str_repeat('a', 36))); $this->assertEquals(false, $this->object->isValid(str_repeat('a', 256))); // Internal keys diff --git a/tests/unit/Validator/LabelTest.php b/tests/unit/Validator/LabelTest.php index 3d9bf7576..a6dd50bef 100644 --- a/tests/unit/Validator/LabelTest.php +++ b/tests/unit/Validator/LabelTest.php @@ -59,7 +59,7 @@ public function testValues(): void $this->assertEquals(false, $this->object->isValid('as=5dasdasdas')); // At most 255 chars - $this->assertEquals(true, $this->object->isValid(str_repeat('a', 255))); + $this->assertEquals(true, $this->object->isValid(str_repeat('a', 36))); $this->assertEquals(false, $this->object->isValid(str_repeat('a', 256))); } } diff --git a/tests/unit/Validator/PermissionsTest.php b/tests/unit/Validator/PermissionsTest.php index 505e69dec..70f2df0f1 100644 --- a/tests/unit/Validator/PermissionsTest.php +++ b/tests/unit/Validator/PermissionsTest.php @@ -248,25 +248,25 @@ public function testInvalidPermissions(): void // team:$value, member:$value and user:$value must have valid Key for $value // No leading special chars $this->assertFalse($object->isValid([Permission::read(Role::user('_1234'))])); - $this->assertEquals('Role "user" identifier value is invalid: Parameter must contain at most 255 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); + $this->assertEquals('Role "user" identifier value is invalid: Parameter must contain at most 36 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); $this->assertFalse($object->isValid([Permission::read(Role::team('-1234'))])); - $this->assertEquals('Role "team" identifier value is invalid: Parameter must contain at most 255 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); + $this->assertEquals('Role "team" identifier value is invalid: Parameter must contain at most 36 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); $this->assertFalse($object->isValid([Permission::read(Role::member('.1234'))])); - $this->assertEquals('Role "member" identifier value is invalid: Parameter must contain at most 255 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); + $this->assertEquals('Role "member" identifier value is invalid: Parameter must contain at most 36 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); // No unsupported special characters $this->assertFalse($object->isValid([Permission::read(Role::user('12$4'))])); - $this->assertEquals('Role "user" identifier value is invalid: Parameter must contain at most 255 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); + $this->assertEquals('Role "user" identifier value is invalid: Parameter must contain at most 36 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); $this->assertFalse($object->isValid([Permission::read(Role::user('12&4'))])); - $this->assertEquals('Role "user" identifier value is invalid: Parameter must contain at most 255 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); + $this->assertEquals('Role "user" identifier value is invalid: Parameter must contain at most 36 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); $this->assertFalse($object->isValid([Permission::read(Role::user('ab(124'))])); - $this->assertEquals('Role "user" identifier value is invalid: Parameter must contain at most 255 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); + $this->assertEquals('Role "user" identifier value is invalid: Parameter must contain at most 36 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); - // Shorter than 255 chars + // Shorter than 36 chars - $this->assertTrue($object->isValid([Permission::read(Role::user(ID::custom(str_repeat('a', 255))))])); + $this->assertTrue($object->isValid([Permission::read(Role::user(ID::custom(str_repeat('a', 36))))])); $this->assertFalse($object->isValid([Permission::read(Role::user(ID::custom(str_repeat('a', 256))))])); - $this->assertEquals('Role "user" identifier value is invalid: Parameter must contain at most 255 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); + $this->assertEquals('Role "user" identifier value is invalid: Parameter must contain at most 36 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); // Permission role must begin with one of: member, role, team, user $this->assertFalse($object->isValid(['update("memmber:1234")'])); @@ -278,7 +278,7 @@ public function testInvalidPermissions(): void // Team permission $this->assertFalse($object->isValid([Permission::read(Role::team(ID::custom('_abcd')))])); - $this->assertEquals('Role "team" identifier value is invalid: Parameter must contain at most 255 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); + $this->assertEquals('Role "team" identifier value is invalid: Parameter must contain at most 36 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); $this->assertFalse($object->isValid([Permission::read(Role::team(ID::custom('abcd/')))])); $this->assertEquals('Dimension must not be empty', $object->getDescription()); $this->assertFalse($object->isValid([Permission::read(Role::team(ID::custom(''), 'abcd'))])); @@ -288,9 +288,9 @@ public function testInvalidPermissions(): void $this->assertFalse($object->isValid([Permission::read(Role::team(ID::custom('abcd'), 'e/fgh'))])); $this->assertEquals('Only one dimension can be provided', $object->getDescription()); $this->assertFalse($object->isValid([Permission::read(Role::team(ID::custom('ab&cd3'), 'efgh'))])); - $this->assertEquals('Role "team" identifier value is invalid: Parameter must contain at most 255 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); + $this->assertEquals('Role "team" identifier value is invalid: Parameter must contain at most 36 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); $this->assertFalse($object->isValid([Permission::read(Role::team(ID::custom('abcd'), 'ef*gh'))])); - $this->assertEquals('Role "team" dimension value is invalid: Parameter must contain at most 255 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); + $this->assertEquals('Role "team" dimension value is invalid: Parameter must contain at most 36 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); // Permission-list length must be valid $object = new Permissions(100);