1
0
Fork 0
mirror of synced 2024-06-03 11:24:48 +12:00

Merge remote-tracking branch 'origin/1.5.x' into refactor-disallow-new-session-with-existing

This commit is contained in:
Jake Barnby 2024-02-25 23:31:04 +13:00
commit 7027c3296a
No known key found for this signature in database
GPG key ID: C437A8CC85B96E9C
3 changed files with 211 additions and 14 deletions

View file

@ -1616,6 +1616,54 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati
$key ??= $relatedCollectionId;
$twoWayKey ??= $collectionId;
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId);
$collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
if ($collection->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$relatedCollectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId);
$relatedCollection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollectionDocument->getInternalId());
if ($relatedCollection->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$attributes = $collection->getAttribute('attributes', []);
/** @var Document[] $attributes */
foreach ($attributes as $attribute) {
if ($attribute->getAttribute('type') !== Database::VAR_RELATIONSHIP) {
continue;
}
if (\strtolower($attribute->getId()) === \strtolower($key)) {
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS);
}
if (
\strtolower($attribute->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey) &&
$attribute->getAttribute('options')['relatedCollection'] === $relatedCollection->getId()
) {
// Console should provide a unique twoWayKey input!
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.');
}
if (
$type === Database::RELATION_MANY_TO_MANY &&
$attribute->getAttribute('options')['relationType'] === Database::RELATION_MANY_TO_MANY &&
$attribute->getAttribute('options')['relatedCollection'] === $relatedCollection->getId()
) {
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Creating more than one "manyToMany" relationship on the same collection is currently not permitted.');
}
}
$attribute = createAttribute(
$databaseId,
$collectionId,

View file

@ -1599,30 +1599,20 @@ App::delete('/v1/users/:userId/mfa/:type')
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User ID.')
->param('type', null, new WhiteList(['totp']), 'Type of authenticator.')
->param('otp', '', new Text(256), 'Valid verification token.')
->inject('requestTimestamp')
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $userId, string $type, string $otp, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $userId, string $type, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
}
$success = match ($type) {
'totp' => Challenge\TOTP::verify($user, $otp),
default => false
};
if (!$success) {
throw new Exception(Exception::USER_INVALID_TOKEN);
}
if (!$user->getAttribute('totp')) {
throw new Exception(Exception::GENERAL_UNKNOWN, 'TOTP not added.');
}
if (!$user->getAttribute('totp')) {
throw new Exception(Exception::GENERAL_UNKNOWN, 'TOTP not added.');
}
$user
->setAttribute('totp', false)

View file

@ -6,6 +6,7 @@ use Tests\E2E\Client;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\SideClient;
use Utopia\Database\Database;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
@ -316,6 +317,164 @@ class DatabasesCustomClientTest extends Scope
$this->assertEquals('restrict', $collection1RelationAttribute['onDelete']);
}
public function testRelationshipSameTwoWayKey(): void
{
$database = $this->client->call(Client::METHOD_POST, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Same two way key'
]);
$databaseId = $database['body']['$id'];
$collection1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::unique(),
'name' => 'c1',
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
$collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::unique(),
'name' => 'c2',
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
\sleep(2);
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection2['body']['$id'],
'type' => Database::RELATION_ONE_TO_ONE,
'twoWay' => false,
'onDelete' => 'cascade',
'key' => 'attr1',
'twoWayKey' => 'same_key'
]);
\sleep(2);
$this->assertEquals(202, $relation['headers']['status-code']);
$this->assertEquals('same_key', $relation['body']['twoWayKey']);
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection2['body']['$id'],
'type' => Database::RELATION_ONE_TO_MANY,
'twoWay' => false,
'onDelete' => 'cascade',
'key' => 'attr2',
'twoWayKey' => 'same_key'
]);
\sleep(2);
$this->assertEquals(409, $relation['body']['code']);
$this->assertEquals('Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.', $relation['body']['message']);
// twoWayKey is null TwoWayKey is default
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection2['body']['$id'],
'type' => Database::RELATION_ONE_TO_MANY,
'twoWay' => false,
'onDelete' => 'cascade',
'key' => 'attr3',
]);
\sleep(2);
$this->assertEquals(202, $relation['headers']['status-code']);
$this->assertArrayHasKey('twoWayKey', $relation['body']);
// twoWayKey is null, TwoWayKey is default, second POST
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection2['body']['$id'],
'type' => Database::RELATION_ONE_TO_MANY,
'twoWay' => false,
'onDelete' => 'cascade',
'key' => 'attr4',
]);
\sleep(2);
$this->assertEquals('Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.', $relation['body']['message']);
$this->assertEquals(409, $relation['body']['code']);
// RelationshipManyToMany
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection2['body']['$id'],
'type' => Database::RELATION_MANY_TO_MANY,
'twoWay' => true,
'onDelete' => 'setNull',
'key' => 'songs',
'twoWayKey' => 'playlist',
]);
\sleep(2);
$this->assertEquals(202, $relation['headers']['status-code']);
$this->assertArrayHasKey('twoWayKey', $relation['body']);
// Second RelationshipManyToMany on Same collections
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection2['body']['$id'],
'type' => Database::RELATION_MANY_TO_MANY,
'twoWay' => true,
'onDelete' => 'setNull',
'key' => 'songs2',
'twoWayKey' => 'playlist2',
]);
\sleep(2);
$this->assertEquals(409, $relation['body']['code']);
$this->assertEquals('Creating more than one "manyToMany" relationship on the same collection is currently not permitted.', $relation['body']['message']);
}
public function testUpdateWithoutRelationPermission(): void
{
$userId = $this->getUser()['$id'];