Fixed document not being unique
This commit is contained in:
parent
97ee872c05
commit
4f11e171db
|
@ -1,3 +1,10 @@
|
|||
# Version 0.5.3 (PRE-RELEASE)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed bug where multiple unique attribute were allowed
|
||||
* Blocked forms from being submitted unlimited times
|
||||
|
||||
# Version 0.5.2 (PRE-RELEASE)
|
||||
|
||||
## Bug Fixes
|
||||
|
|
|
@ -18,6 +18,7 @@ use Auth\Auth;
|
|||
use Auth\Validator\Password;
|
||||
use Database\Database;
|
||||
use Database\Document;
|
||||
use Database\Exception\Duplicate;
|
||||
use Database\Validator\UID;
|
||||
use Database\Validator\Authorization;
|
||||
use DeviceDetector\DeviceDetector;
|
||||
|
@ -88,21 +89,25 @@ $utopia->post('/v1/account')
|
|||
|
||||
Authorization::disable();
|
||||
|
||||
$user = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_USERS,
|
||||
'$permissions' => [
|
||||
'read' => ['*'],
|
||||
'write' => ['user:{self}'],
|
||||
],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'status' => Auth::USER_STATUS_UNACTIVATED,
|
||||
'password' => Auth::passwordHash($password),
|
||||
'password-update' => time(),
|
||||
'registration' => time(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
]);
|
||||
try {
|
||||
$user = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_USERS,
|
||||
'$permissions' => [
|
||||
'read' => ['*'],
|
||||
'write' => ['user:{self}'],
|
||||
],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'status' => Auth::USER_STATUS_UNACTIVATED,
|
||||
'password' => Auth::passwordHash($password),
|
||||
'password-update' => time(),
|
||||
'registration' => time(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
], ['email' => $email]);
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Account already exists', 409);
|
||||
}
|
||||
|
||||
Authorization::enable();
|
||||
|
||||
|
@ -391,18 +396,22 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
if (!$user || empty($user->getId())) { // Last option -> create user alone, generate random password
|
||||
Authorization::disable();
|
||||
|
||||
$user = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_USERS,
|
||||
'$permissions' => ['read' => ['*'], 'write' => ['user:{self}']],
|
||||
'email' => $email,
|
||||
'emailVerification' => true,
|
||||
'status' => Auth::USER_STATUS_ACTIVATED, // Email should already be authenticated by OAuth2 provider
|
||||
'password' => Auth::passwordHash(Auth::passwordGenerator()),
|
||||
'password-update' => time(),
|
||||
'registration' => time(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
]);
|
||||
try {
|
||||
$user = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_USERS,
|
||||
'$permissions' => ['read' => ['*'], 'write' => ['user:{self}']],
|
||||
'email' => $email,
|
||||
'emailVerification' => true,
|
||||
'status' => Auth::USER_STATUS_ACTIVATED, // Email should already be authenticated by OAuth2 provider
|
||||
'password' => Auth::passwordHash(Auth::passwordGenerator()),
|
||||
'password-update' => time(),
|
||||
'registration' => time(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
], ['email' => $email]);
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Account already exists', 409);
|
||||
}
|
||||
|
||||
Authorization::enable();
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ use Database\Database;
|
|||
use Database\Document;
|
||||
use Database\Validator\UID;
|
||||
use Database\Validator\Authorization;
|
||||
use Database\Exception\Duplicate;
|
||||
use Template\Template;
|
||||
use Auth\Auth;
|
||||
|
||||
|
@ -243,22 +244,26 @@ $utopia->post('/v1/teams/:teamId/memberships')
|
|||
|
||||
Authorization::disable();
|
||||
|
||||
$invitee = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_USERS,
|
||||
'$permissions' => [
|
||||
'read' => ['user:{self}', '*'],
|
||||
'write' => ['user:{self}'],
|
||||
],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'status' => Auth::USER_STATUS_UNACTIVATED,
|
||||
'password' => Auth::passwordHash(Auth::passwordGenerator()),
|
||||
'password-update' => time(),
|
||||
'registration' => time(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
'tokens' => [],
|
||||
]);
|
||||
try {
|
||||
$invitee = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_USERS,
|
||||
'$permissions' => [
|
||||
'read' => ['user:{self}', '*'],
|
||||
'write' => ['user:{self}'],
|
||||
],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'status' => Auth::USER_STATUS_UNACTIVATED,
|
||||
'password' => Auth::passwordHash(Auth::passwordGenerator()),
|
||||
'password-update' => time(),
|
||||
'registration' => time(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
'tokens' => [],
|
||||
], ['email' => $email]);
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Account already exists', 409);
|
||||
}
|
||||
|
||||
Authorization::reset();
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ use Utopia\Audit\Audit;
|
|||
use Utopia\Audit\Adapters\MySQL as AuditAdapter;
|
||||
use Utopia\Locale\Locale;
|
||||
use Database\Database;
|
||||
use Database\Exception\Duplicate;
|
||||
use Database\Validator\UID;
|
||||
use DeviceDetector\DeviceDetector;
|
||||
use GeoIp2\Database\Reader;
|
||||
|
@ -46,21 +47,25 @@ $utopia->post('/v1/users')
|
|||
throw new Exception('User already registered', 409);
|
||||
}
|
||||
|
||||
$user = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_USERS,
|
||||
'$permissions' => [
|
||||
'read' => ['*'],
|
||||
'write' => ['user:{self}'],
|
||||
],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'status' => Auth::USER_STATUS_UNACTIVATED,
|
||||
'password' => Auth::passwordHash($password),
|
||||
'password-update' => time(),
|
||||
'registration' => time(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
]);
|
||||
try {
|
||||
$user = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_USERS,
|
||||
'$permissions' => [
|
||||
'read' => ['*'],
|
||||
'write' => ['user:{self}'],
|
||||
],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'status' => Auth::USER_STATUS_UNACTIVATED,
|
||||
'password' => Auth::passwordHash($password),
|
||||
'password-update' => time(),
|
||||
'registration' => time(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
], ['email' => $email]);
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Account already exists', 409);
|
||||
}
|
||||
|
||||
$oauth2Keys = [];
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ USE `appwrite`;
|
|||
CREATE TABLE IF NOT EXISTS `template.abuse.abuse` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`_key` varchar(255) NOT NULL,
|
||||
`_time` varchar(45) NOT NULL,
|
||||
`_time` int(11) NOT NULL,
|
||||
`_count` int(11) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `unique1` (`_key`,`_time`),
|
||||
|
@ -15,7 +15,6 @@ CREATE TABLE IF NOT EXISTS `template.abuse.abuse` (
|
|||
CREATE TABLE IF NOT EXISTS `template.audit.audit` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`userId` varchar(45) NOT NULL,
|
||||
`userType` int(11) NOT NULL,
|
||||
`event` varchar(45) NOT NULL,
|
||||
`resource` varchar(45) DEFAULT NULL,
|
||||
`userAgent` text NOT NULL,
|
||||
|
@ -25,7 +24,7 @@ CREATE TABLE IF NOT EXISTS `template.audit.audit` (
|
|||
`data` longtext DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `id_UNIQUE` (`id`),
|
||||
KEY `index_1` (`userId`,`userType`),
|
||||
KEY `index_1` (`userId`),
|
||||
KEY `index_2` (`event`),
|
||||
KEY `index_3` (`resource`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
@ -74,10 +73,18 @@ CREATE TABLE IF NOT EXISTS `template.database.relationships` (
|
|||
KEY `relationships_end_nodes_id_idx` (`end`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `template.database.unique` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`key` varchar(128) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `index1` (`key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
/* Default App */
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `app_console.database.documents` LIKE `template.database.documents`;
|
||||
CREATE TABLE IF NOT EXISTS `app_console.database.properties` LIKE `template.database.properties`;
|
||||
CREATE TABLE IF NOT EXISTS `app_console.database.relationships` LIKE `template.database.relationships`;
|
||||
CREATE TABLE IF NOT EXISTS `app_console.database.unique` LIKE `template.database.unique`;
|
||||
CREATE TABLE IF NOT EXISTS `app_console.audit.audit` LIKE `template.audit.audit`;
|
||||
CREATE TABLE IF NOT EXISTS `app_console.abuse.abuse` LIKE `template.abuse.abuse`;
|
|
@ -26,7 +26,7 @@ const APP_USERAGENT = APP_NAME.'-Server v%s. Please report abuse at %s';
|
|||
const APP_MODE_ADMIN = 'admin';
|
||||
const APP_PAGING_LIMIT = 15;
|
||||
const APP_CACHE_BUSTER = 55;
|
||||
const APP_VERSION_STABLE = '0.5.2';
|
||||
const APP_VERSION_STABLE = '0.5.3';
|
||||
const APP_STORAGE_UPLOADS = '/storage/uploads';
|
||||
const APP_STORAGE_CACHE = '/storage/cache';
|
||||
const APP_STORAGE_CERTIFICATES = '/storage/certificates';
|
||||
|
|
|
@ -68,6 +68,15 @@ $callbacks = [
|
|||
|
||||
try {
|
||||
$statement = $db->prepare("
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `template.database.unique` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`key` varchar(128) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `index1` (`key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `{$schema}`.`app_{$project->getId()}.database.unique` LIKE `template.database.unique`;
|
||||
ALTER TABLE `{$schema}`.`app_{$project->getId()}.audit.audit` DROP COLUMN IF EXISTS `userType`;
|
||||
ALTER TABLE `{$schema}`.`app_{$project->getId()}.audit.audit` DROP INDEX IF EXISTS `index_1`;
|
||||
ALTER TABLE `{$schema}`.`app_{$project->getId()}.audit.audit` ADD INDEX IF NOT EXISTS `index_1` (`userId` ASC);
|
||||
|
|
|
@ -84,7 +84,7 @@ services:
|
|||
- _APP_SMTP_PORT=25
|
||||
|
||||
mariadb:
|
||||
image: appwrite/mariadb:1.0.2 # fix issues when upgrading using: mysql_upgrade -u root -p
|
||||
image: appwrite/mariadb:1.0.3 # fix issues when upgrading using: mysql_upgrade -u root -p
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
|
|
|
@ -68,7 +68,7 @@ abstract class Adapter
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function createDocument(array $data);
|
||||
abstract public function createDocument(array $data, array $unique);
|
||||
|
||||
/**
|
||||
* Update Document.
|
||||
|
|
|
@ -7,6 +7,7 @@ use Exception;
|
|||
use PDO;
|
||||
use Redis as Client;
|
||||
use Database\Adapter;
|
||||
use Database\Exception\Duplicate;
|
||||
use Database\Validator\Authorization;
|
||||
|
||||
class MySQL extends Adapter
|
||||
|
@ -150,7 +151,7 @@ class MySQL extends Adapter
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public function createDocument(array $data = [])
|
||||
public function createDocument(array $data = [], array $unique = [])
|
||||
{
|
||||
$order = 0;
|
||||
$data = array_merge(['$id' => null, '$permissions' => []], $data); // Merge data with default params
|
||||
|
@ -178,6 +179,21 @@ class MySQL extends Adapter
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Unique Keys
|
||||
*/
|
||||
foreach($unique as $key => $value) {
|
||||
$st = $this->getPDO()->prepare('INSERT INTO `'.$this->getNamespace().'.database.unique`
|
||||
SET `key` = :key;
|
||||
');
|
||||
|
||||
$st->bindValue(':key', md5($data['$collection'].':'.$key.'='.$value), PDO::PARAM_STR);
|
||||
|
||||
if(!$st->execute()) {
|
||||
throw new Duplicate('Duplicated Property: '.$key.'='.$value);
|
||||
}
|
||||
}
|
||||
|
||||
// Add or update fields abstraction level
|
||||
$st1 = $this->getPDO()->prepare('INSERT INTO `'.$this->getNamespace().'.database.documents`
|
||||
SET uid = :uid, createdAt = :createdAt, updatedAt = :updatedAt, signature = :signature, revision = :revision, permissions = :permissions, status = 0
|
||||
|
@ -397,6 +413,7 @@ class MySQL extends Adapter
|
|||
$documents = 'app_'.$namespace.'.database.documents';
|
||||
$properties = 'app_'.$namespace.'.database.properties';
|
||||
$relationships = 'app_'.$namespace.'.database.relationships';
|
||||
$unique = 'app_'.$namespace.'.database.unique';
|
||||
$audit = 'app_'.$namespace.'.audit.audit';
|
||||
$abuse = 'app_'.$namespace.'.abuse.abuse';
|
||||
|
||||
|
@ -404,6 +421,7 @@ class MySQL extends Adapter
|
|||
$this->getPDO()->prepare('CREATE TABLE `'.$documents.'` LIKE `template.database.documents`;')->execute();
|
||||
$this->getPDO()->prepare('CREATE TABLE `'.$properties.'` LIKE `template.database.properties`;')->execute();
|
||||
$this->getPDO()->prepare('CREATE TABLE `'.$relationships.'` LIKE `template.database.relationships`;')->execute();
|
||||
$this->getPDO()->prepare('CREATE TABLE `'.$unique.'` LIKE `template.database.unique`;')->execute();
|
||||
$this->getPDO()->prepare('CREATE TABLE `'.$audit.'` LIKE `template.audit.audit`;')->execute();
|
||||
$this->getPDO()->prepare('CREATE TABLE `'.$abuse.'` LIKE `template.abuse.abuse`;')->execute();
|
||||
} catch (Exception $e) {
|
||||
|
|
|
@ -105,9 +105,9 @@ class Redis extends Adapter
|
|||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function createDocument(array $data = [])
|
||||
public function createDocument(array $data = [], array $unique = [])
|
||||
{
|
||||
$data = $this->adapter->createDocument($data);
|
||||
$data = $this->adapter->createDocument($data, $unique);
|
||||
|
||||
$this->getRedis()->expire($this->getNamespace().':document-'.$data['$id'], 0);
|
||||
$this->getRedis()->expire($this->getNamespace().':document-'.$data['$id'], 0);
|
||||
|
|
|
@ -182,7 +182,7 @@ class Database
|
|||
* @throws AuthorizationException
|
||||
* @throws StructureException
|
||||
*/
|
||||
public function createDocument(array $data)
|
||||
public function createDocument(array $data, array $unique = [])
|
||||
{
|
||||
$document = new Document($data);
|
||||
|
||||
|
@ -198,7 +198,7 @@ class Database
|
|||
throw new StructureException($validator->getDescription()); // var_dump($validator->getDescription()); return false;
|
||||
}
|
||||
|
||||
return new Document($this->adapter->createDocument($data));
|
||||
return new Document($this->adapter->createDocument($data, $unique));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
7
src/Database/Exception/Duplicate.php
Normal file
7
src/Database/Exception/Duplicate.php
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Exception;
|
||||
|
||||
class Duplicate extends \Exception
|
||||
{
|
||||
}
|
Loading…
Reference in a new issue