mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
2824 lines
117 KiB
PHP
2824 lines
117 KiB
PHP
<?php
|
|
|
|
namespace Tests\E2E\Services\Users;
|
|
|
|
use Appwrite\Tests\Retry;
|
|
use Appwrite\Utopia\Response;
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
use Tests\E2E\Client;
|
|
use Utopia\Database\Document;
|
|
use Utopia\Database\Helpers\ID;
|
|
use Utopia\Database\Query;
|
|
|
|
trait UsersBase
|
|
{
|
|
/**
|
|
* Static caches for test data
|
|
*/
|
|
private static array $cachedUser = [];
|
|
private static array $cachedHashedPasswordUsers = [];
|
|
private static array $cachedUserTarget = [];
|
|
private static bool $userNameUpdated = false;
|
|
private static bool $userEmailUpdated = false;
|
|
private static bool $userNumberUpdated = false;
|
|
|
|
/**
|
|
* Helper to get or create a base test user
|
|
*/
|
|
protected function setupUser(): array
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
if (!empty(self::$cachedUser[$projectId])) {
|
|
return self::$cachedUser[$projectId];
|
|
}
|
|
|
|
$user = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'userId' => ID::unique(),
|
|
'email' => 'cristiano.ronaldo@manchester-united.co.uk',
|
|
'password' => 'password',
|
|
'name' => 'Cristiano Ronaldo',
|
|
]);
|
|
|
|
if ($user['headers']['status-code'] === 409) {
|
|
// User already exists, fetch by searching
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'search' => 'cristiano.ronaldo@manchester-united.co.uk',
|
|
]);
|
|
|
|
if (!empty($response['body']['users'])) {
|
|
self::$cachedUser[$projectId] = ['userId' => $response['body']['users'][0]['$id']];
|
|
return self::$cachedUser[$projectId];
|
|
}
|
|
}
|
|
|
|
if ($user['headers']['status-code'] === 201) {
|
|
self::$cachedUser[$projectId] = ['userId' => $user['body']['$id']];
|
|
}
|
|
|
|
return self::$cachedUser[$projectId];
|
|
}
|
|
|
|
/**
|
|
* Helper to create user1 (Lionel Messi)
|
|
*/
|
|
protected function setupUser1(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
|
|
$res = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'userId' => ID::custom('user1'),
|
|
'email' => 'lionel.messi@psg.fr',
|
|
'password' => 'password',
|
|
'name' => 'Lionel Messi',
|
|
]);
|
|
|
|
// Ignore 409 conflict - user already exists
|
|
}
|
|
|
|
/**
|
|
* Helper to create all hashed password users for testing
|
|
*/
|
|
protected function setupHashedPasswordUsers(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
if (!empty(self::$cachedHashedPasswordUsers[$projectId])) {
|
|
return;
|
|
}
|
|
|
|
// MD5 user
|
|
$this->client->call(Client::METHOD_POST, '/users/md5', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'userId' => 'md5',
|
|
'email' => 'md5@appwrite.io',
|
|
'password' => '144fa7eaa4904e8ee120651997f70dcc', // appwrite
|
|
'name' => 'MD5 User',
|
|
]);
|
|
|
|
// Bcrypt user
|
|
$this->client->call(Client::METHOD_POST, '/users/bcrypt', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'userId' => 'bcrypt',
|
|
'email' => 'bcrypt@appwrite.io',
|
|
'password' => '$2a$15$xX/myGbFU.ZSKHSi6EHdBOySTdYm8QxBLXmOPHrYMwV0mHRBBSBOq', // appwrite (15 rounds)
|
|
'name' => 'Bcrypt User',
|
|
]);
|
|
|
|
// Argon2 user
|
|
$this->client->call(Client::METHOD_POST, '/users/argon2', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'userId' => 'argon2',
|
|
'email' => 'argon2@appwrite.io',
|
|
'password' => '$argon2i$v=19$m=20,t=3,p=2$YXBwd3JpdGU$A/54i238ed09ZR4NwlACU5XnkjNBZU9QeOEuhjLiexI', // appwrite (salt appwrite, parallel 2, memory 20, iterations 3, length 32)
|
|
'name' => 'Argon2 User',
|
|
]);
|
|
|
|
// SHA512 user
|
|
$this->client->call(Client::METHOD_POST, '/users/sha', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'userId' => 'sha512',
|
|
'email' => 'sha512@appwrite.io',
|
|
'password' => '4243da0a694e8a2f727c8060fe0507c8fa01ca68146c76d2c190805b638c20c6bf6ba04e21f11ae138785d0bff63c416e6f87badbffad37f6dee50094cc38c70', // appwrite (sha512)
|
|
'name' => 'SHA512 User',
|
|
'passwordVersion' => 'sha512'
|
|
]);
|
|
|
|
// Scrypt user
|
|
$this->client->call(Client::METHOD_POST, '/users/scrypt', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'userId' => 'scrypt',
|
|
'email' => 'scrypt@appwrite.io',
|
|
'password' => '3fdef49701bc4cfaacd551fe017283513284b4731e6945c263246ef948d3cf63b5d269c31fd697246085111a428245e24a4ddc6b64c687bc60a8910dbafc1d5b', // appwrite (salt appwrite, cpu 16384, memory 13, parallel 2, length 64)
|
|
'name' => 'Scrypt User',
|
|
'passwordSalt' => 'appwrite',
|
|
'passwordCpu' => 16384,
|
|
'passwordMemory' => 13,
|
|
'passwordParallel' => 2,
|
|
'passwordLength' => 64
|
|
]);
|
|
|
|
// PHPass user
|
|
$this->client->call(Client::METHOD_POST, '/users/phpass', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'userId' => 'phpass',
|
|
'email' => 'phpass@appwrite.io',
|
|
'password' => '$P$Br387rwferoKN7uwHZqNMu98q3U8RO.',
|
|
'name' => 'PHPass User',
|
|
]);
|
|
|
|
// Scrypt Modified user
|
|
$this->client->call(Client::METHOD_POST, '/users/scrypt-modified', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'userId' => 'scrypt-modified',
|
|
'email' => 'scrypt-modified@appwrite.io',
|
|
'password' => 'UlM7JiXRcQhzAGlaonpSqNSLIz475WMddOgLjej5De9vxTy48K6WtqlEzrRFeK4t0COfMhWCb8wuMHgxOFCHFQ==', // appwrite
|
|
'name' => 'Scrypt Modified User',
|
|
'passwordSalt' => 'UxLMreBr6tYyjQ==',
|
|
'passwordSaltSeparator' => 'Bw==',
|
|
'passwordSignerKey' => 'XyEKE9RcTDeLEsL/RjwPDBv/RqDl8fb3gpYEOQaPihbxf1ZAtSOHCjuAAa7Q3oHpCYhXSN9tizHgVOwn6krflQ==',
|
|
]);
|
|
|
|
self::$cachedHashedPasswordUsers[$projectId] = true;
|
|
}
|
|
|
|
/**
|
|
* Helper to create or get a user target
|
|
*/
|
|
protected function setupUserTarget(): array
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
if (!empty(self::$cachedUserTarget[$projectId])) {
|
|
return self::$cachedUserTarget[$projectId];
|
|
}
|
|
|
|
$data = $this->setupUser();
|
|
|
|
// Create provider
|
|
$provider = $this->client->call(Client::METHOD_POST, '/messaging/providers/sendgrid', \array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'providerId' => ID::unique(),
|
|
'name' => 'Sengrid1',
|
|
'apiKey' => 'my-apikey',
|
|
'from' => 'from@domain.com',
|
|
]);
|
|
|
|
if ($provider['headers']['status-code'] !== 201) {
|
|
// Provider may already exist, try to find it
|
|
$providers = $this->client->call(Client::METHOD_GET, '/messaging/providers', \array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()));
|
|
|
|
foreach ($providers['body']['providers'] ?? [] as $p) {
|
|
if ($p['name'] === 'Sengrid1') {
|
|
$provider = ['body' => $p];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create target
|
|
$response = $this->client->call(Client::METHOD_POST, '/users/' . $data['userId'] . '/targets', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'targetId' => ID::unique(),
|
|
'providerId' => $provider['body']['$id'],
|
|
'providerType' => 'email',
|
|
'identifier' => 'random-email@mail.org',
|
|
]);
|
|
|
|
if ($response['headers']['status-code'] === 201) {
|
|
self::$cachedUserTarget[$projectId] = $response['body'];
|
|
}
|
|
|
|
return self::$cachedUserTarget[$projectId] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Helper to ensure user name is updated (for search tests)
|
|
*/
|
|
protected function ensureUserNameUpdated(): array
|
|
{
|
|
$data = $this->setupUser();
|
|
$projectId = $this->getProject()['$id'];
|
|
|
|
if (self::$userNameUpdated) {
|
|
return $data;
|
|
}
|
|
|
|
$this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/name', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'name' => 'Updated name',
|
|
]);
|
|
|
|
self::$userNameUpdated = true;
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Helper to ensure user email is updated (for search and password tests)
|
|
*/
|
|
protected function ensureUserEmailUpdated(): array
|
|
{
|
|
$data = $this->setupUser();
|
|
$projectId = $this->getProject()['$id'];
|
|
|
|
if (self::$userEmailUpdated) {
|
|
return $data;
|
|
}
|
|
|
|
$this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/email', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'email' => 'users.service@updated.com',
|
|
]);
|
|
|
|
self::$userEmailUpdated = true;
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Helper to ensure user phone number is updated (for search tests)
|
|
*/
|
|
protected function ensureUserNumberUpdated(): array
|
|
{
|
|
$data = $this->setupUser();
|
|
$projectId = $this->getProject()['$id'];
|
|
|
|
if (self::$userNumberUpdated) {
|
|
return $data;
|
|
}
|
|
|
|
$this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/phone', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'number' => '+910000000000',
|
|
]);
|
|
|
|
self::$userNumberUpdated = true;
|
|
return $data;
|
|
}
|
|
|
|
public function testCreateUser(): void
|
|
{
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$user = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => ID::unique(),
|
|
'email' => 'cristiano.ronaldo@manchester-united.co.uk',
|
|
'password' => 'password',
|
|
'name' => 'Cristiano Ronaldo',
|
|
], false);
|
|
$this->assertEquals($user['headers']['status-code'], 201);
|
|
|
|
// Test empty prefs is object not array
|
|
$bodyString = $user['body'];
|
|
$prefs = substr($bodyString, strpos($bodyString, '"prefs":') + 8, 2);
|
|
$this->assertEquals('{}', $prefs);
|
|
|
|
$body = json_decode($bodyString, true);
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 201);
|
|
$this->assertEquals($body['name'], 'Cristiano Ronaldo');
|
|
$this->assertEquals($body['email'], 'cristiano.ronaldo@manchester-united.co.uk');
|
|
$this->assertEquals($body['status'], true);
|
|
$this->assertGreaterThan('2000-01-01 00:00:00', $body['registration']);
|
|
$this->assertEquals($body['labels'], []);
|
|
|
|
/**
|
|
* Test Create with Custom ID for SUCCESS
|
|
*/
|
|
$res = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => ID::custom('user1'),
|
|
'email' => 'lionel.messi@psg.fr',
|
|
'password' => 'password',
|
|
'name' => 'Lionel Messi',
|
|
]);
|
|
|
|
$this->assertEquals($res['headers']['status-code'], 201);
|
|
$this->assertEquals($res['body']['$id'], 'user1');
|
|
$this->assertEquals($res['body']['name'], 'Lionel Messi');
|
|
$this->assertEquals($res['body']['email'], 'lionel.messi@psg.fr');
|
|
$this->assertEquals(true, $res['body']['status']);
|
|
$this->assertGreaterThan('2000-01-01 00:00:00', $res['body']['registration']);
|
|
|
|
/**
|
|
* Test Create with hashed passwords
|
|
*/
|
|
$res = $this->client->call(Client::METHOD_POST, '/users/md5', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => 'md5',
|
|
'email' => 'md5@appwrite.io',
|
|
'password' => '144fa7eaa4904e8ee120651997f70dcc', // appwrite
|
|
'name' => 'MD5 User',
|
|
]);
|
|
|
|
$res = $this->client->call(Client::METHOD_POST, '/users/bcrypt', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => 'bcrypt',
|
|
'email' => 'bcrypt@appwrite.io',
|
|
'password' => '$2a$15$xX/myGbFU.ZSKHSi6EHdBOySTdYm8QxBLXmOPHrYMwV0mHRBBSBOq', // appwrite (15 rounds)
|
|
'name' => 'Bcrypt User',
|
|
]);
|
|
|
|
$this->assertEquals($res['headers']['status-code'], 201);
|
|
$this->assertEquals($res['body']['password'], '$2a$15$xX/myGbFU.ZSKHSi6EHdBOySTdYm8QxBLXmOPHrYMwV0mHRBBSBOq');
|
|
$this->assertEquals($res['body']['hash'], 'bcrypt');
|
|
|
|
$res = $this->client->call(Client::METHOD_POST, '/users/argon2', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => 'argon2',
|
|
'email' => 'argon2@appwrite.io',
|
|
'password' => '$argon2i$v=19$m=20,t=3,p=2$YXBwd3JpdGU$A/54i238ed09ZR4NwlACU5XnkjNBZU9QeOEuhjLiexI', // appwrite (salt appwrite, parallel 2, memory 20, iterations 3, length 32)
|
|
'name' => 'Argon2 User',
|
|
]);
|
|
|
|
$this->assertEquals($res['headers']['status-code'], 201);
|
|
$this->assertEquals($res['body']['password'], '$argon2i$v=19$m=20,t=3,p=2$YXBwd3JpdGU$A/54i238ed09ZR4NwlACU5XnkjNBZU9QeOEuhjLiexI');
|
|
$this->assertEquals($res['body']['hash'], 'argon2');
|
|
|
|
$res = $this->client->call(Client::METHOD_POST, '/users/sha', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => 'sha512',
|
|
'email' => 'sha512@appwrite.io',
|
|
'password' => '4243da0a694e8a2f727c8060fe0507c8fa01ca68146c76d2c190805b638c20c6bf6ba04e21f11ae138785d0bff63c416e6f87badbffad37f6dee50094cc38c70', // appwrite (sha512)
|
|
'name' => 'SHA512 User',
|
|
'passwordVersion' => 'sha512'
|
|
]);
|
|
|
|
$this->assertEquals($res['headers']['status-code'], 201);
|
|
$this->assertEquals($res['body']['password'], '4243da0a694e8a2f727c8060fe0507c8fa01ca68146c76d2c190805b638c20c6bf6ba04e21f11ae138785d0bff63c416e6f87badbffad37f6dee50094cc38c70');
|
|
$this->assertEquals($res['body']['hash'], 'sha');
|
|
$this->assertEquals($res['body']['hashOptions']['version'], 'sha512');
|
|
|
|
$res = $this->client->call(Client::METHOD_POST, '/users/scrypt', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => 'scrypt',
|
|
'email' => 'scrypt@appwrite.io',
|
|
'password' => '3fdef49701bc4cfaacd551fe017283513284b4731e6945c263246ef948d3cf63b5d269c31fd697246085111a428245e24a4ddc6b64c687bc60a8910dbafc1d5b', // appwrite (salt appwrite, cpu 16384, memory 13, parallel 2, length 64)
|
|
'name' => 'Scrypt User',
|
|
'passwordSalt' => 'appwrite',
|
|
'passwordCpu' => 16384,
|
|
'passwordMemory' => 13,
|
|
'passwordParallel' => 2,
|
|
'passwordLength' => 64
|
|
]);
|
|
|
|
$this->assertEquals($res['headers']['status-code'], 201);
|
|
$this->assertEquals($res['body']['password'], '3fdef49701bc4cfaacd551fe017283513284b4731e6945c263246ef948d3cf63b5d269c31fd697246085111a428245e24a4ddc6b64c687bc60a8910dbafc1d5b');
|
|
$this->assertEquals($res['body']['hash'], 'scrypt');
|
|
$this->assertEquals($res['body']['hashOptions']['salt'], 'appwrite');
|
|
$this->assertEquals($res['body']['hashOptions']['costCpu'], 16384);
|
|
$this->assertEquals($res['body']['hashOptions']['costMemory'], 13);
|
|
$this->assertEquals($res['body']['hashOptions']['costParallel'], 2);
|
|
$this->assertEquals($res['body']['hashOptions']['length'], 64);
|
|
|
|
$res = $this->client->call(Client::METHOD_POST, '/users/phpass', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => 'phpass',
|
|
'email' => 'phpass@appwrite.io',
|
|
'password' => '$P$Br387rwferoKN7uwHZqNMu98q3U8RO.',
|
|
'name' => 'PHPass User',
|
|
]);
|
|
|
|
$this->assertEquals($res['headers']['status-code'], 201);
|
|
$this->assertEquals($res['body']['password'], '$P$Br387rwferoKN7uwHZqNMu98q3U8RO.');
|
|
|
|
$res = $this->client->call(Client::METHOD_POST, '/users/scrypt-modified', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => 'scrypt-modified',
|
|
'email' => 'scrypt-modified@appwrite.io',
|
|
'password' => 'UlM7JiXRcQhzAGlaonpSqNSLIz475WMddOgLjej5De9vxTy48K6WtqlEzrRFeK4t0COfMhWCb8wuMHgxOFCHFQ==', // appwrite
|
|
'name' => 'Scrypt Modified User',
|
|
'passwordSalt' => 'UxLMreBr6tYyjQ==',
|
|
'passwordSaltSeparator' => 'Bw==',
|
|
'passwordSignerKey' => 'XyEKE9RcTDeLEsL/RjwPDBv/RqDl8fb3gpYEOQaPihbxf1ZAtSOHCjuAAa7Q3oHpCYhXSN9tizHgVOwn6krflQ==',
|
|
]);
|
|
|
|
$this->assertEquals($res['headers']['status-code'], 201);
|
|
$this->assertEquals($res['body']['password'], 'UlM7JiXRcQhzAGlaonpSqNSLIz475WMddOgLjej5De9vxTy48K6WtqlEzrRFeK4t0COfMhWCb8wuMHgxOFCHFQ==');
|
|
$this->assertEquals($res['body']['hash'], 'scryptMod');
|
|
$this->assertEquals($res['body']['hashOptions']['salt'], 'UxLMreBr6tYyjQ==');
|
|
$this->assertEquals($res['body']['hashOptions']['signerKey'], 'XyEKE9RcTDeLEsL/RjwPDBv/RqDl8fb3gpYEOQaPihbxf1ZAtSOHCjuAAa7Q3oHpCYhXSN9tizHgVOwn6krflQ==');
|
|
$this->assertEquals($res['body']['hashOptions']['saltSeparator'], 'Bw==');
|
|
|
|
// Cache the user ID for other tests
|
|
$projectId = $this->getProject()['$id'];
|
|
self::$cachedUser[$projectId] = ['userId' => $body['$id']];
|
|
}
|
|
|
|
/**
|
|
* Tries to login into all accounts created with hashed password. Ensures hash veifying logic.
|
|
*/
|
|
public function testCreateUserSessionHashed(): void
|
|
{
|
|
$this->setupHashedPasswordUsers();
|
|
$userIds = ['md5', 'bcrypt', 'argon2', 'sha512', 'scrypt', 'phpass', 'scrypt-modified'];
|
|
|
|
foreach ($userIds as $userId) {
|
|
// Ensure sessions can be created with hashed passwords
|
|
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
|
'origin' => 'http://localhost',
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
]), [
|
|
'email' => $userId . '@appwrite.io',
|
|
'password' => 'appwrite',
|
|
]);
|
|
|
|
$this->assertEquals(201, $response['headers']['status-code']);
|
|
$this->assertEquals($userId, $response['body']['userId']);
|
|
}
|
|
|
|
foreach ($userIds as $userId) {
|
|
// Ensure all passwords were re-hashed
|
|
$response = $this->client->call(Client::METHOD_GET, '/users/' . $userId, array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), []);
|
|
|
|
$this->assertEquals(200, $response['headers']['status-code']);
|
|
$this->assertEquals($userId, $response['body']['$id']);
|
|
$this->assertEquals($userId . '@appwrite.io', $response['body']['email']);
|
|
$this->assertEquals('argon2', $response['body']['hash']);
|
|
$this->assertStringStartsWith('$argon2', $response['body']['password']);
|
|
}
|
|
|
|
foreach ($userIds as $userId) {
|
|
// Ensure sessions can be created after re-hashing of passwords
|
|
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
|
'origin' => 'http://localhost',
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
]), [
|
|
'email' => $userId . '@appwrite.io',
|
|
'password' => 'appwrite',
|
|
]);
|
|
|
|
$this->assertEquals(201, $response['headers']['status-code']);
|
|
$this->assertEquals($userId, $response['body']['userId']);
|
|
}
|
|
}
|
|
|
|
public function testCreateToken(): void
|
|
{
|
|
$data = $this->setupUser();
|
|
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$token = $this->client->call(Client::METHOD_POST, '/users/' . $data['userId'] . '/tokens', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals(201, $token['headers']['status-code']);
|
|
$this->assertEquals($data['userId'], $token['body']['userId']);
|
|
$this->assertNotEmpty($token['body']['secret']);
|
|
$this->assertNotEmpty($token['body']['expire']);
|
|
|
|
$token = $this->client->call(Client::METHOD_POST, '/users/' . $data['userId'] . '/tokens', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'length' => 15,
|
|
'expire' => 60,
|
|
]);
|
|
|
|
$this->assertEquals(201, $token['headers']['status-code']);
|
|
$this->assertEquals($data['userId'], $token['body']['userId']);
|
|
$this->assertEquals(15, strlen($token['body']['secret']));
|
|
$this->assertNotEmpty($token['body']['expire']);
|
|
|
|
/**
|
|
* Test for FAILURE
|
|
*/
|
|
$token = $this->client->call(Client::METHOD_POST, '/users/' . $data['userId'] . '/tokens', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'length' => 1,
|
|
'expire' => 1,
|
|
]);
|
|
|
|
$this->assertEquals(400, $token['headers']['status-code']);
|
|
$this->assertArrayNotHasKey('userId', $token['body']);
|
|
$this->assertArrayNotHasKey('secret', $token['body']);
|
|
|
|
$token = $this->client->call(Client::METHOD_POST, '/users/' . $data['userId'] . '/tokens', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'expire' => 999999999999999,
|
|
]);
|
|
|
|
$this->assertEquals(400, $token['headers']['status-code']);
|
|
$this->assertArrayNotHasKey('userId', $token['body']);
|
|
$this->assertArrayNotHasKey('secret', $token['body']);
|
|
}
|
|
|
|
public function testCreateSession(): void
|
|
{
|
|
$data = $this->setupUser();
|
|
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$response = $this->client->call(Client::METHOD_POST, '/users/' . $data['userId'] . '/sessions', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals(201, $response['headers']['status-code']);
|
|
|
|
$session = $response['body'];
|
|
$this->assertEquals($data['userId'], $session['userId']);
|
|
$this->assertNotEmpty($session['secret']);
|
|
$this->assertNotEmpty($session['expire']);
|
|
$this->assertEquals('server', $session['provider']);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/account', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-session' => $session['secret']
|
|
]);
|
|
|
|
$this->assertEquals(200, $response['headers']['status-code']);
|
|
|
|
$response = $this->client->call(Client::METHOD_DELETE, '/account/sessions/current', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-session' => $session['secret']
|
|
]);
|
|
|
|
$this->assertEquals(204, $response['headers']['status-code']);
|
|
}
|
|
|
|
|
|
/**
|
|
* Tests all optional parameters of createUser (email, phone, anonymous..)
|
|
*/
|
|
public function testCreateUserTypes(): void
|
|
{
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
|
|
// Email + password
|
|
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => 'unique()',
|
|
'email' => 'emailuser@appwrite.io',
|
|
'password' => 'emailUserPassword',
|
|
]);
|
|
|
|
$this->assertNotEmpty($response['body']['email']);
|
|
$this->assertNotEmpty($response['body']['password']);
|
|
$this->assertEmpty($response['body']['phone']);
|
|
|
|
// Phone
|
|
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => 'unique()',
|
|
'phone' => '+123456789012',
|
|
]);
|
|
|
|
$this->assertEmpty($response['body']['email']);
|
|
$this->assertEmpty($response['body']['password']);
|
|
$this->assertNotEmpty($response['body']['phone']);
|
|
|
|
// Anonymous
|
|
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => 'unique()',
|
|
]);
|
|
|
|
$this->assertEmpty($response['body']['email']);
|
|
$this->assertEmpty($response['body']['password']);
|
|
$this->assertEmpty($response['body']['phone']);
|
|
|
|
// Email-only
|
|
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => 'unique()',
|
|
'email' => 'emailonlyuser@appwrite.io',
|
|
]);
|
|
|
|
$this->assertNotEmpty($response['body']['email']);
|
|
$this->assertEmpty($response['body']['password']);
|
|
$this->assertEmpty($response['body']['phone']);
|
|
|
|
// Password-only
|
|
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => 'unique()',
|
|
'password' => 'passwordOnlyUser',
|
|
]);
|
|
|
|
$this->assertEmpty($response['body']['email']);
|
|
$this->assertNotEmpty($response['body']['password']);
|
|
$this->assertEmpty($response['body']['phone']);
|
|
|
|
// Password and phone
|
|
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => 'unique()',
|
|
'password' => 'passwordOnlyUser',
|
|
'phone' => '+123456789013',
|
|
]);
|
|
|
|
$this->assertEmpty($response['body']['email']);
|
|
$this->assertNotEmpty($response['body']['password']);
|
|
$this->assertNotEmpty($response['body']['phone']);
|
|
}
|
|
|
|
public function testListUsers(): void
|
|
{
|
|
$data = $this->setupUser();
|
|
$this->setupUser1();
|
|
$this->setupHashedPasswordUsers();
|
|
// In --functional mode, this test runs independently with 9 users created above
|
|
// (setupUser: 1 + setupUser1: 1 + setupHashedPasswordUsers: 7)
|
|
// In sequential mode, there may be more users from other tests
|
|
$minUsers = 9;
|
|
|
|
/**
|
|
* Test for SUCCESS listUsers
|
|
*/
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertGreaterThanOrEqual($minUsers, count($response['body']['users']));
|
|
|
|
// Find our users by ID instead of assuming position
|
|
$userIds = array_column($response['body']['users'], '$id');
|
|
$this->assertContains($data['userId'], $userIds);
|
|
$this->assertContains('user1', $userIds);
|
|
|
|
// Find user1 for later use in queries
|
|
$user1 = null;
|
|
foreach ($response['body']['users'] as $user) {
|
|
if ($user['$id'] === 'user1') {
|
|
$user1 = $user;
|
|
break;
|
|
}
|
|
}
|
|
$this->assertNotNull($user1, 'user1 should exist in user list');
|
|
|
|
// This test ensures that by default, endpoints dont support select queries
|
|
// If we add select query to this endpoint, you will need to remove this test
|
|
// Please make sure to add it to another place, unless all endpoints support select queries
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::select(['name'])->toString()
|
|
]
|
|
]);
|
|
$this->assertEquals($response['headers']['status-code'], 400);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::equal('name', [$user1['name']])->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
$this->assertEquals($response['body']['users'][0]['name'], $user1['name']);
|
|
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::equal('name', [$user1['name']])->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
$this->assertEquals($response['body']['users'][0]['email'], $user1['email']);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::equal('status', [true])->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
// In parallel mode, count may vary - just ensure our known users are present
|
|
$this->assertGreaterThanOrEqual($minUsers, count($response['body']['users']));
|
|
// Verify our test users are in the results by ID
|
|
$userIds = array_column($response['body']['users'], '$id');
|
|
$this->assertContains($data['userId'], $userIds);
|
|
$this->assertContains('user1', $userIds);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::equal('status', [false])->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertEmpty($response['body']['users']);
|
|
$this->assertCount(0, $response['body']['users']);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::equal('passwordUpdate', [$user1['passwordUpdate']])->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
$this->assertEquals($response['body']['users'][0]['passwordUpdate'], $user1['passwordUpdate']);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::equal('registration', [$user1['registration']])->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
$this->assertEquals($response['body']['users'][0]['registration'], $user1['registration']);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::equal('emailVerification', [false])->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
// In parallel mode, count may vary - just ensure our known users are present
|
|
$this->assertGreaterThanOrEqual($minUsers, count($response['body']['users']));
|
|
// Verify our test users are in the results by ID
|
|
$userIds = array_column($response['body']['users'], '$id');
|
|
$this->assertContains($data['userId'], $userIds);
|
|
$this->assertContains('user1', $userIds);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::equal('emailVerification', [true])->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertEmpty($response['body']['users']);
|
|
$this->assertCount(0, $response['body']['users']);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::equal('phoneVerification', [false])->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertIsArray($response['body']['users']);
|
|
$this->assertGreaterThanOrEqual($minUsers, count($response['body']['users']));
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::equal('phoneVerification', [true])->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertEmpty($response['body']['users']);
|
|
$this->assertCount(0, $response['body']['users']);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::cursorAfter(new Document(['$id' => $data['userId']]))->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
// CursorAfter should return results, count varies in parallel mode
|
|
$this->assertGreaterThanOrEqual(1, count($response['body']['users']));
|
|
// First result after cursor should be user1 (created right after setupUser)
|
|
$this->assertEquals($response['body']['users'][0]['$id'], 'user1');
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::cursorBefore(new Document(['$id' => 'user1']))->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
|
|
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
|
|
|
/**
|
|
* Test for SUCCESS searchUsers
|
|
*/
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'search' => "Ronaldo",
|
|
]);
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'search' => "cristiano.ronaldo@manchester-united.co.uk",
|
|
]);
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'search' => "cristiano.ronaldo",
|
|
]);
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'search' => "manchester",
|
|
]);
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'search' => "united.co.uk",
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertIsArray($response['body']);
|
|
$this->assertIsArray($response['body']['users']);
|
|
$this->assertIsInt($response['body']['total']);
|
|
$this->assertEquals(1, $response['body']['total']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
|
|
// Some databases only support fulltext search on complete words
|
|
if ($this->getSupportForFulltextWildcard()) {
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'search' => "man",
|
|
]);
|
|
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertIsArray($response['body']);
|
|
$this->assertIsArray($response['body']['users']);
|
|
$this->assertIsInt($response['body']['total']);
|
|
$this->assertEquals(1, $response['body']['total']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
}
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'search' => $data['userId'],
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'search' => '>',
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertEmpty($response['body']['users']);
|
|
$this->assertCount(0, $response['body']['users']);
|
|
|
|
/**
|
|
* Test for FAILURE
|
|
*/
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::cursorAfter(new Document(['$id' => 'unknown']))->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals(400, $response['headers']['status-code']);
|
|
}
|
|
|
|
public function testGetUser(): void
|
|
{
|
|
$data = $this->setupUser();
|
|
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'], array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($user['body']['name']);
|
|
$this->assertNotEmpty($user['body']['email']);
|
|
$this->assertEquals($user['body']['status'], true);
|
|
$this->assertGreaterThan('2000-01-01 00:00:00', $user['body']['registration']);
|
|
|
|
$sessions = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/sessions', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($sessions['headers']['status-code'], 200);
|
|
$this->assertIsArray($sessions['body']);
|
|
|
|
$users = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($users['headers']['status-code'], 200);
|
|
$this->assertIsArray($users['body']);
|
|
$this->assertIsArray($users['body']['users']);
|
|
$this->assertIsInt($users['body']['total']);
|
|
$this->assertGreaterThan(0, $users['body']['total']);
|
|
|
|
/**
|
|
* Test for SUCCESS with total=false
|
|
*/
|
|
$usersWithIncludeTotalFalse = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'total' => false
|
|
]);
|
|
|
|
$this->assertEquals(200, $usersWithIncludeTotalFalse['headers']['status-code']);
|
|
$this->assertIsArray($usersWithIncludeTotalFalse['body']);
|
|
$this->assertIsArray($usersWithIncludeTotalFalse['body']['users']);
|
|
$this->assertIsInt($usersWithIncludeTotalFalse['body']['total']);
|
|
$this->assertEquals(0, $usersWithIncludeTotalFalse['body']['total']);
|
|
$this->assertGreaterThan(0, count($usersWithIncludeTotalFalse['body']['users']));
|
|
|
|
/**
|
|
* Test for FAILURE
|
|
*/
|
|
$user = $this->client->call(Client::METHOD_GET, '/users/non_existent', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 404);
|
|
$this->assertEquals($user['body']['code'], 404);
|
|
$this->assertEquals($user['body']['message'], 'User with the requested ID could not be found.');
|
|
$this->assertEquals($user['body']['type'], 'user_not_found');
|
|
}
|
|
|
|
public function testListUserMemberships(): void
|
|
{
|
|
$data = $this->setupUser();
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
|
|
// create a new team
|
|
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'teamId' => 'unique()',
|
|
'name' => 'Test Team',
|
|
]);
|
|
|
|
// create a new membership
|
|
$membership = $this->client->call(Client::METHOD_POST, '/teams/' . $team['body']['$id'] . '/memberships', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => $data['userId'],
|
|
'roles' => ['new-role'],
|
|
]);
|
|
|
|
// list the memberships
|
|
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/memberships', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertEquals($response['body']['memberships'][0]['$id'], $membership['body']['$id']);
|
|
$this->assertEquals($response['body']['memberships'][0]['roles'], ['new-role']);
|
|
$this->assertEquals($response['body']['total'], 1);
|
|
|
|
// create another membership with a new role
|
|
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'teamId' => 'unique()',
|
|
'name' => 'Test Team 2',
|
|
]);
|
|
|
|
$membership = $this->client->call(Client::METHOD_POST, '/teams/' . $team['body']['$id'] . '/memberships', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => $data['userId'],
|
|
'roles' => ['new-role-2'],
|
|
]);
|
|
|
|
// list out memberships and query by role
|
|
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/memberships', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::contains('roles', ['new-role-2'])->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertEquals($response['body']['memberships'][0]['$id'], $membership['body']['$id']);
|
|
$this->assertEquals($response['body']['memberships'][0]['roles'], ['new-role-2']);
|
|
$this->assertEquals($response['body']['total'], 1);
|
|
|
|
/**
|
|
* Test for FAILURE
|
|
*/
|
|
|
|
// query using equal on array field
|
|
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/memberships', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::equal('roles', ['new-role-2'])->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['body']['code'], 400);
|
|
$this->assertEquals($response['body']['message'], 'Invalid `queries` param: Invalid query: Cannot query equal on attribute "roles" because it is an array.');
|
|
$this->assertEquals($response['body']['type'], 'general_argument_invalid');
|
|
}
|
|
|
|
public function testUpdateUserName(): void
|
|
{
|
|
$data = $this->setupUser();
|
|
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/name', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'name' => '',
|
|
]);
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['name'], '');
|
|
|
|
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'], array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['name'], '');
|
|
|
|
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/name', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'name' => 'Updated name',
|
|
]);
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['name'], 'Updated name');
|
|
|
|
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'], array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['name'], 'Updated name');
|
|
|
|
// Mark name as updated for search tests
|
|
self::$userNameUpdated = true;
|
|
}
|
|
|
|
public function testUpdateUserNameSearch(): void
|
|
{
|
|
$data = $this->ensureUserNameUpdated();
|
|
$id = $data['userId'] ?? '';
|
|
$newName = 'Updated name';
|
|
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'search' => $newName,
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
$this->assertEquals($response['body']['users'][0]['$id'], $id);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'search' => $id,
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
$this->assertEquals($response['body']['users'][0]['$id'], $id);
|
|
}
|
|
|
|
public function testUpdateUserEmail(): void
|
|
{
|
|
$data = $this->setupUser();
|
|
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/email', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'email' => '',
|
|
]);
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['email'], '');
|
|
|
|
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'], array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['email'], '');
|
|
|
|
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/email', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'email' => 'users.service@updated.com',
|
|
]);
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['email'], 'users.service@updated.com');
|
|
|
|
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'], array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['email'], 'users.service@updated.com');
|
|
|
|
// Mark email as updated for search tests
|
|
self::$userEmailUpdated = true;
|
|
}
|
|
|
|
public function testUpdateUserEmailSearch(): void
|
|
{
|
|
$data = $this->ensureUserEmailUpdated();
|
|
$id = $data['userId'] ?? '';
|
|
$newEmail = '"users.service@updated.com"';
|
|
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'search' => $newEmail,
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
$this->assertEquals($response['body']['users'][0]['$id'], $id);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'search' => $id,
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
$this->assertEquals($response['body']['users'][0]['$id'], $id);
|
|
}
|
|
|
|
public function testUpdateUserPassword(): void
|
|
{
|
|
$data = $this->ensureUserEmailUpdated();
|
|
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], [
|
|
'email' => 'users.service@updated.com',
|
|
'password' => 'password'
|
|
]);
|
|
|
|
$this->assertEquals($session['headers']['status-code'], 201);
|
|
|
|
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/password', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'password' => '',
|
|
]);
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($user['body']['$id']);
|
|
$this->assertEmpty($user['body']['password']);
|
|
|
|
$this->assertEventually(function () {
|
|
$session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], [
|
|
'email' => 'users.service@updated.com',
|
|
'password' => 'password'
|
|
]);
|
|
|
|
$this->assertEquals(401, $session['headers']['status-code']);
|
|
}, 15_000, 500);
|
|
$this->updateProjectinvalidateSessionsProperty(true);
|
|
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/password', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'password' => 'password2',
|
|
]);
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($user['body']['$id']);
|
|
$this->assertNotEmpty($user['body']['password']);
|
|
|
|
$sessions = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/sessions', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($sessions['headers']['status-code'], 200);
|
|
$this->assertIsArray($sessions['body']);
|
|
$this->assertEmpty($sessions['body']['sessions']);
|
|
|
|
$session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], [
|
|
'email' => 'users.service@updated.com',
|
|
'password' => 'password2'
|
|
]);
|
|
|
|
$this->assertEquals($session['headers']['status-code'], 201);
|
|
$this->updateProjectinvalidateSessionsProperty(false);
|
|
}
|
|
|
|
#[Retry(count: 1)]
|
|
public function testUpdateUserStatus(): void
|
|
{
|
|
$data = $this->setupUser();
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/status', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'status' => false,
|
|
]);
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['status'], false);
|
|
|
|
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'], array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['status'], false);
|
|
|
|
// Reset status back to true for other tests
|
|
$this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/status', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'status' => true,
|
|
]);
|
|
}
|
|
|
|
public function testUpdateEmailVerification(): void
|
|
{
|
|
$data = $this->setupUser();
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/verification', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'emailVerification' => true,
|
|
]);
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['emailVerification'], true);
|
|
|
|
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'], array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['emailVerification'], true);
|
|
}
|
|
|
|
#[Retry(count: 1)]
|
|
public function testUpdateAndGetUserPrefs(): void
|
|
{
|
|
$data = $this->setupUser();
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/prefs', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'prefs' => [
|
|
'funcKey1' => 'funcValue1',
|
|
'funcKey2' => 'funcValue2',
|
|
],
|
|
]);
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['funcKey1'], 'funcValue1');
|
|
$this->assertEquals($user['body']['funcKey2'], 'funcValue2');
|
|
|
|
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/prefs', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body'], [
|
|
'funcKey1' => 'funcValue1',
|
|
'funcKey2' => 'funcValue2',
|
|
]);
|
|
|
|
/**
|
|
* Test for FAILURE
|
|
*/
|
|
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/prefs', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'prefs' => 'bad-string',
|
|
]);
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 400);
|
|
|
|
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/prefs', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 400);
|
|
}
|
|
|
|
public function testUpdateUserNumber(): void
|
|
{
|
|
$data = $this->setupUser();
|
|
$this->setupUser1();
|
|
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$updatedNumber = "";
|
|
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/phone', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'number' => $updatedNumber,
|
|
]);
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEmpty($user['body']['phone'] ?? '');
|
|
|
|
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'], array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEmpty($user['body']['phone'] ?? '');
|
|
|
|
$updatedNumber = "+910000000000"; //dummy number
|
|
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/phone', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'number' => $updatedNumber,
|
|
]);
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['phone'], $updatedNumber);
|
|
|
|
$user = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'], array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 200);
|
|
$this->assertEquals($user['body']['phone'], $updatedNumber);
|
|
|
|
/**
|
|
* Test for FAILURE
|
|
*/
|
|
|
|
$errorType = "user_target_already_exists";
|
|
$user1Id = "user1";
|
|
$statusCodeForUserPhoneAlredyExists = 409;
|
|
|
|
// adding same number ($updatedNumber) to different user i.e user1
|
|
$response = $this->client->call(Client::METHOD_PATCH, '/users/' . $user1Id . '/phone', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'number' => $updatedNumber,
|
|
]);
|
|
$this->assertEquals($response['headers']['status-code'], $statusCodeForUserPhoneAlredyExists);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertEquals($response['body']['type'], $errorType);
|
|
|
|
// Mark phone as updated for search tests
|
|
self::$userNumberUpdated = true;
|
|
}
|
|
|
|
public function testUpdateTwoUsersPhoneToEmpty(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
$headers = array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders());
|
|
|
|
// Create two users with distinct valid phone numbers
|
|
$user1 = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'user1-phone-empty-test@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'User One',
|
|
'phone' => '+16175551201',
|
|
]);
|
|
$this->assertEquals(201, $user1['headers']['status-code']);
|
|
$this->assertEquals('+16175551201', $user1['body']['phone']);
|
|
|
|
$user2 = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'user2-phone-empty-test@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'User Two',
|
|
'phone' => '+16175551202',
|
|
]);
|
|
$this->assertEquals(201, $user2['headers']['status-code']);
|
|
$this->assertEquals('+16175551202', $user2['body']['phone']);
|
|
|
|
// Update first user's phone to empty - must succeed
|
|
$response1 = $this->client->call(Client::METHOD_PATCH, '/users/' . $user1['body']['$id'] . '/phone', $headers, [
|
|
'number' => '',
|
|
]);
|
|
$this->assertEquals(200, $response1['headers']['status-code'], 'First user phone should update to empty');
|
|
$this->assertEmpty($response1['body']['phone'] ?? '');
|
|
|
|
// Update second user's phone to empty - must succeed (would fail with duplicate if empty was stored as '')
|
|
$response2 = $this->client->call(Client::METHOD_PATCH, '/users/' . $user2['body']['$id'] . '/phone', $headers, [
|
|
'number' => '',
|
|
]);
|
|
$this->assertEquals(200, $response2['headers']['status-code'], 'Second user phone should update to empty without duplicate error');
|
|
$this->assertEmpty($response2['body']['phone'] ?? '');
|
|
|
|
// Verify both users have empty phone via GET
|
|
$get1 = $this->client->call(Client::METHOD_GET, '/users/' . $user1['body']['$id'], $headers);
|
|
$get2 = $this->client->call(Client::METHOD_GET, '/users/' . $user2['body']['$id'], $headers);
|
|
$this->assertEquals(200, $get1['headers']['status-code']);
|
|
$this->assertEquals(200, $get2['headers']['status-code']);
|
|
$this->assertEmpty($get1['body']['phone'] ?? '');
|
|
$this->assertEmpty($get2['body']['phone'] ?? '');
|
|
}
|
|
|
|
public function testUpdateUserNumberSearch(): void
|
|
{
|
|
$data = $this->ensureUserNumberUpdated();
|
|
$id = $data['userId'] ?? '';
|
|
$newNumber = "+910000000000"; //dummy number
|
|
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'search' => $newNumber,
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 200);
|
|
$this->assertNotEmpty($response['body']);
|
|
$this->assertNotEmpty($response['body']['users']);
|
|
$this->assertCount(1, $response['body']['users']);
|
|
$this->assertEquals($response['body']['users'][0]['$id'], $id);
|
|
$this->assertEquals($response['body']['users'][0]['phone'], $newNumber);
|
|
}
|
|
|
|
public static function userLabelsProvider(): array
|
|
{
|
|
return [
|
|
'single label' => [
|
|
['admin'],
|
|
Response::STATUS_CODE_OK,
|
|
['admin'],
|
|
],
|
|
'replace with multiple labels' => [
|
|
['vip', 'pro'],
|
|
Response::STATUS_CODE_OK,
|
|
['vip', 'pro'],
|
|
],
|
|
'clear labels' => [
|
|
[],
|
|
Response::STATUS_CODE_OK,
|
|
[],
|
|
],
|
|
'duplicate labels' => [
|
|
['vip', 'vip', 'pro'],
|
|
Response::STATUS_CODE_OK,
|
|
['vip', 'pro'],
|
|
],
|
|
'invalid label' => [
|
|
['invalid-label'],
|
|
Response::STATUS_CODE_BAD_REQUEST,
|
|
[],
|
|
],
|
|
'too long' => [
|
|
[\str_repeat('a', 129)],
|
|
Response::STATUS_CODE_BAD_REQUEST,
|
|
[],
|
|
],
|
|
'too many labels' => [
|
|
[\array_fill(0, 101, 'a')],
|
|
Response::STATUS_CODE_BAD_REQUEST,
|
|
[],
|
|
],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('userLabelsProvider')]
|
|
public function testUpdateUserLabels(array $labels, int $expectedStatus, array $expectedLabels): void
|
|
{
|
|
$data = $this->setupUser();
|
|
|
|
$user = $this->client->call(Client::METHOD_PUT, '/users/' . $data['userId'] . '/labels', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'labels' => $labels,
|
|
]);
|
|
|
|
$this->assertEquals($expectedStatus, $user['headers']['status-code']);
|
|
if ($expectedStatus === Response::STATUS_CODE_OK) {
|
|
$this->assertEquals($user['body']['labels'], $expectedLabels);
|
|
}
|
|
}
|
|
|
|
public function testUpdateUserLabelsWithoutLabels(): void
|
|
{
|
|
$data = $this->setupUser();
|
|
|
|
$user = $this->client->call(Client::METHOD_PUT, '/users/' . $data['userId'] . '/labels', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), []);
|
|
|
|
$this->assertEquals(Response::STATUS_CODE_BAD_REQUEST, $user['headers']['status-code']);
|
|
}
|
|
|
|
public function testUpdateUserLabelsNonExistentUser(): void
|
|
{
|
|
$user = $this->client->call(Client::METHOD_PUT, '/users/dne/labels', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'labels' => ['admin'],
|
|
]);
|
|
|
|
$this->assertEquals(Response::STATUS_CODE_NOT_FOUND, $user['headers']['status-code']);
|
|
}
|
|
|
|
|
|
public function testGetLogs(): void
|
|
{
|
|
$data = $this->setupUser();
|
|
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
|
$this->assertIsArray($logs['body']['logs']);
|
|
$this->assertIsNumeric($logs['body']['total']);
|
|
|
|
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::limit(1)->toString()
|
|
],
|
|
]);
|
|
|
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
|
$this->assertIsArray($logs['body']['logs']);
|
|
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
|
$this->assertIsNumeric($logs['body']['total']);
|
|
|
|
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::offset(1)->toString()
|
|
],
|
|
]);
|
|
|
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
|
$this->assertIsArray($logs['body']['logs']);
|
|
$this->assertIsNumeric($logs['body']['total']);
|
|
|
|
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::limit(1)->toString(),
|
|
Query::offset(1)->toString(),
|
|
],
|
|
]);
|
|
|
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
|
$this->assertIsArray($logs['body']['logs']);
|
|
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
|
$this->assertIsNumeric($logs['body']['total']);
|
|
|
|
/**
|
|
* Test for FAILURE
|
|
*/
|
|
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::limit(-1)->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 400);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::offset(-1)->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 400);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::equal('$id', ['asdf'])->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 400);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
Query::orderAsc('$id')->toString()
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 400);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'queries' => [
|
|
'{ "method": "cursorAsc", "attribute": "$id" }'
|
|
]
|
|
]);
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 400);
|
|
}
|
|
|
|
public function testCreateUserTarget(): void
|
|
{
|
|
$data = $this->setupUser();
|
|
|
|
$provider = $this->client->call(Client::METHOD_POST, '/messaging/providers/sendgrid', \array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'providerId' => ID::unique(),
|
|
'name' => 'Sengrid1',
|
|
'apiKey' => 'my-apikey',
|
|
'from' => 'from@domain.com',
|
|
]);
|
|
$this->assertEquals(201, $provider['headers']['status-code']);
|
|
$response = $this->client->call(Client::METHOD_POST, '/users/' . $data['userId'] . '/targets', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'targetId' => ID::unique(),
|
|
'providerId' => $provider['body']['$id'],
|
|
'providerType' => 'email',
|
|
'identifier' => 'random-email@mail.org',
|
|
]);
|
|
$this->assertEquals(201, $response['headers']['status-code']);
|
|
$this->assertEquals($provider['body']['$id'], $response['body']['providerId']);
|
|
$this->assertEquals('random-email@mail.org', $response['body']['identifier']);
|
|
|
|
// Cache for other tests
|
|
$projectId = $this->getProject()['$id'];
|
|
self::$cachedUserTarget[$projectId] = $response['body'];
|
|
}
|
|
|
|
public function testUpdateUserTarget(): void
|
|
{
|
|
$data = $this->setupUserTarget();
|
|
|
|
$response = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/targets/' . $data['$id'], array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'identifier' => 'random-email1@mail.org',
|
|
]);
|
|
$this->assertEquals(200, $response['headers']['status-code']);
|
|
$this->assertEquals('random-email1@mail.org', $response['body']['identifier']);
|
|
$this->assertEquals(false, $response['body']['expired']);
|
|
|
|
// Update cache with new data
|
|
$projectId = $this->getProject()['$id'];
|
|
self::$cachedUserTarget[$projectId] = $response['body'];
|
|
}
|
|
|
|
public function testListUserTarget(): void
|
|
{
|
|
$data = $this->setupUserTarget();
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/targets', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals(200, $response['headers']['status-code']);
|
|
$this->assertGreaterThanOrEqual(1, \count($response['body']['targets']));
|
|
}
|
|
|
|
public function testGetUserTarget(): void
|
|
{
|
|
$data = $this->setupUserTarget();
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/targets/' . $data['$id'], array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
$this->assertEquals(200, $response['headers']['status-code']);
|
|
$this->assertEquals($data['$id'], $response['body']['$id']);
|
|
}
|
|
|
|
public function testDeleteUserTarget(): void
|
|
{
|
|
$data = $this->setupUserTarget();
|
|
|
|
$response = $this->client->call(Client::METHOD_DELETE, '/users/' . $data['userId'] . '/targets/' . $data['$id'], array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals(204, $response['headers']['status-code']);
|
|
|
|
// Clear cached target since it was deleted
|
|
$projectId = $this->getProject()['$id'];
|
|
unset(self::$cachedUserTarget[$projectId]);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/targets', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals(200, $response['headers']['status-code']);
|
|
}
|
|
|
|
public function testDeleteUser(): void
|
|
{
|
|
// Create a new user specifically for deletion test
|
|
$userId = ID::unique();
|
|
$user = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => $userId,
|
|
'email' => 'deletetest@example.com',
|
|
'password' => 'password',
|
|
'name' => 'Delete Test User',
|
|
]);
|
|
|
|
$this->assertEquals(201, $user['headers']['status-code']);
|
|
|
|
/**
|
|
* Test for SUCCESS
|
|
*/
|
|
$user = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId, array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 204);
|
|
|
|
/**
|
|
* Test for FAILURE
|
|
*/
|
|
$user = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId, array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($user['headers']['status-code'], 404);
|
|
}
|
|
|
|
public function testUserJWT()
|
|
{
|
|
// Create user
|
|
$userId = ID::unique();
|
|
$user = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'userId' => $userId,
|
|
'email' => 'jwtuser@appwrite.io',
|
|
'password' => 'password',
|
|
], false);
|
|
$this->assertEquals($user['headers']['status-code'], 201);
|
|
|
|
// Create JWT 0, with no session available
|
|
$response = $this->client->call(Client::METHOD_POST, '/users/' . $userId . '/jwts', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), []);
|
|
|
|
$this->assertEquals(201, $response['headers']['status-code']);
|
|
$this->assertNotEmpty($response['body']['jwt']);
|
|
$jwt0 = $response['body']['jwt'];
|
|
|
|
// Ensure JWT 0 works
|
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
|
'origin' => 'http://localhost',
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-jwt' => $jwt0,
|
|
]));
|
|
|
|
$this->assertEquals(200, $response['headers']['status-code']);
|
|
$this->assertEquals($userId, $response['body']['$id']);
|
|
|
|
// Create two sessions
|
|
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
|
'origin' => 'http://localhost',
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
]), [
|
|
'email' => 'jwtuser@appwrite.io',
|
|
'password' => 'password',
|
|
]);
|
|
|
|
$this->assertEquals(201, $response['headers']['status-code']);
|
|
$this->assertEquals($userId, $response['body']['userId']);
|
|
$this->assertNotEmpty($response['body']['$id']);
|
|
$session1Id = $response['body']['$id'];
|
|
|
|
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
|
'origin' => 'http://localhost',
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
]), [
|
|
'email' => 'jwtuser@appwrite.io',
|
|
'password' => 'password',
|
|
]);
|
|
|
|
$this->assertEquals(201, $response['headers']['status-code']);
|
|
$this->assertEquals($userId, $response['body']['userId']);
|
|
$this->assertNotEmpty($response['body']['$id']);
|
|
$session2Id = $response['body']['$id'];
|
|
|
|
// Create JWT 1 for older session by ID
|
|
$response = $this->client->call(Client::METHOD_POST, '/users/' . $userId . '/jwts', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'sessionId' => $session1Id
|
|
]);
|
|
|
|
$this->assertEquals(201, $response['headers']['status-code']);
|
|
$this->assertNotEmpty($response['body']['jwt']);
|
|
$jwt1 = $response['body']['jwt'];
|
|
|
|
// Ensure JWT 1 works
|
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
|
'origin' => 'http://localhost',
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-jwt' => $jwt1,
|
|
]));
|
|
|
|
$this->assertEquals(200, $response['headers']['status-code']);
|
|
$this->assertEquals($userId, $response['body']['$id']);
|
|
|
|
// Create JWT 2 for latest session using 'current' param
|
|
$response = $this->client->call(Client::METHOD_POST, '/users/' . $userId . '/jwts', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'duration' => 5,
|
|
'sessionId' => 'current'
|
|
]);
|
|
|
|
$this->assertEquals(201, $response['headers']['status-code']);
|
|
$this->assertNotEmpty($response['body']['jwt']);
|
|
$jwt2 = $response['body']['jwt'];
|
|
|
|
// Ensure JWT 2 works
|
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
|
'origin' => 'http://localhost',
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-jwt' => $jwt2,
|
|
]));
|
|
|
|
$this->assertEquals(200, $response['headers']['status-code']);
|
|
$this->assertEquals($userId, $response['body']['$id']);
|
|
|
|
// Wait, ensure JWT 2 no longer works because of short duration
|
|
|
|
\sleep(10);
|
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
|
'origin' => 'http://localhost',
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-jwt' => $jwt2,
|
|
]));
|
|
|
|
$this->assertEquals(401, $response['headers']['status-code']);
|
|
|
|
// Delete session, ensure JWT 1 no longer works because of session missing
|
|
|
|
$response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId . '/sessions', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'sessionId' => $session1Id
|
|
]);
|
|
|
|
$this->assertEquals(204, $response['headers']['status-code']);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
|
'origin' => 'http://localhost',
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-jwt' => $jwt1,
|
|
]));
|
|
|
|
$this->assertEquals(401, $response['headers']['status-code']);
|
|
|
|
// Ensure JWT 0 works still even with no sessions
|
|
|
|
$response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId . '/sessions', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()), [
|
|
'sessionId' => $session2Id
|
|
]);
|
|
|
|
$this->assertEquals(204, $response['headers']['status-code']);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
|
'origin' => 'http://localhost',
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
'x-appwrite-jwt' => $jwt0,
|
|
]));
|
|
|
|
$this->assertEquals(200, $response['headers']['status-code']);
|
|
$this->assertEquals($userId, $response['body']['$id']);
|
|
|
|
// Cleanup after test
|
|
|
|
$response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId, array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
], $this->getHeaders()));
|
|
|
|
$this->assertEquals($response['headers']['status-code'], 204);
|
|
}
|
|
|
|
// TODO add test for session delete
|
|
// TODO add test for all sessions delete
|
|
|
|
/**
|
|
* Test PATCH /users/:userId/impersonator - set and unset impersonator capability
|
|
*/
|
|
public function testUpdateUserImpersonator(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
$headers = array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders());
|
|
|
|
$user = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'impersonator-update-test@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'Impersonator Update Test',
|
|
]);
|
|
$this->assertEquals(201, $user['headers']['status-code']);
|
|
$userId = $user['body']['$id'];
|
|
|
|
$this->assertFalse($user['body']['impersonator'] ?? false);
|
|
|
|
$updated = $this->client->call(Client::METHOD_PATCH, '/users/' . $userId . '/impersonator', $headers, [
|
|
'impersonator' => true,
|
|
]);
|
|
$this->assertEquals(200, $updated['headers']['status-code']);
|
|
$this->assertTrue($updated['body']['impersonator']);
|
|
$this->assertEquals($userId, $updated['body']['$id']);
|
|
|
|
$get = $this->client->call(Client::METHOD_GET, '/users/' . $userId, $headers);
|
|
$this->assertEquals(200, $get['headers']['status-code']);
|
|
$this->assertTrue($get['body']['impersonator']);
|
|
|
|
$updated = $this->client->call(Client::METHOD_PATCH, '/users/' . $userId . '/impersonator', $headers, [
|
|
'impersonator' => false,
|
|
]);
|
|
$this->assertEquals(200, $updated['headers']['status-code']);
|
|
$this->assertFalse($updated['body']['impersonator']);
|
|
|
|
$get = $this->client->call(Client::METHOD_GET, '/users/' . $userId, $headers);
|
|
$this->assertEquals(200, $get['headers']['status-code']);
|
|
$this->assertFalse($get['body']['impersonator']);
|
|
}
|
|
|
|
/**
|
|
* Test impersonation by user ID: session auth + header → act as target user, account returns target + impersonatorUserId
|
|
*/
|
|
public function testImpersonateByUserId(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
$headers = array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders());
|
|
|
|
$userA = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'impersonator-a@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'User A Impersonator',
|
|
]);
|
|
$this->assertEquals(201, $userA['headers']['status-code']);
|
|
$idA = $userA['body']['$id'];
|
|
|
|
$userB = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'impersonator-target-b@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'User B Target',
|
|
]);
|
|
$this->assertEquals(201, $userB['headers']['status-code']);
|
|
$idB = $userB['body']['$id'];
|
|
|
|
$patch = $this->client->call(Client::METHOD_PATCH, '/users/' . $idA . '/impersonator', $headers, ['impersonator' => true]);
|
|
$this->assertEquals(200, $patch['headers']['status-code']);
|
|
|
|
$session = $this->client->call(Client::METHOD_POST, '/users/' . $idA . '/sessions', $headers);
|
|
$this->assertEquals(201, $session['headers']['status-code']);
|
|
$sessionSecret = $session['body']['secret'];
|
|
|
|
$accountHeaders = [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-session' => $sessionSecret,
|
|
'x-appwrite-impersonate-user-id' => $idB,
|
|
];
|
|
|
|
$account = $this->client->call(Client::METHOD_GET, '/account', $accountHeaders);
|
|
$this->assertEquals(200, $account['headers']['status-code']);
|
|
$this->assertEquals($idB, $account['body']['$id']);
|
|
$this->assertEquals('User B Target', $account['body']['name']);
|
|
$this->assertEquals($idA, $account['body']['impersonatorUserId']);
|
|
|
|
$withoutHeader = $this->client->call(Client::METHOD_GET, '/account', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-session' => $sessionSecret,
|
|
]);
|
|
$this->assertEquals(200, $withoutHeader['headers']['status-code']);
|
|
$this->assertEquals($idA, $withoutHeader['body']['$id']);
|
|
$this->assertEquals('User A Impersonator', $withoutHeader['body']['name']);
|
|
$this->assertArrayHasKey('impersonatorUserId', $withoutHeader['body']);
|
|
$this->assertNull($withoutHeader['body']['impersonatorUserId']);
|
|
}
|
|
|
|
/**
|
|
* Test impersonation by email header
|
|
*/
|
|
public function testImpersonateByEmail(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
$headers = array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders());
|
|
|
|
$userA = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'impersonate-by-email-actor@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'Actor',
|
|
]);
|
|
$this->assertEquals(201, $userA['headers']['status-code']);
|
|
$idA = $userA['body']['$id'];
|
|
|
|
$targetEmail = 'impersonate-by-email-target@appwrite.io';
|
|
$userB = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => $targetEmail,
|
|
'password' => 'password',
|
|
'name' => 'Target By Email',
|
|
]);
|
|
$this->assertEquals(201, $userB['headers']['status-code']);
|
|
$idB = $userB['body']['$id'];
|
|
|
|
$this->client->call(Client::METHOD_PATCH, '/users/' . $idA . '/impersonator', $headers, ['impersonator' => true]);
|
|
$session = $this->client->call(Client::METHOD_POST, '/users/' . $idA . '/sessions', $headers);
|
|
$this->assertEquals(201, $session['headers']['status-code']);
|
|
|
|
$account = $this->client->call(Client::METHOD_GET, '/account', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-session' => $session['body']['secret'],
|
|
'x-appwrite-impersonate-user-email' => $targetEmail,
|
|
]);
|
|
$this->assertEquals(200, $account['headers']['status-code']);
|
|
$this->assertEquals($idB, $account['body']['$id']);
|
|
$this->assertEquals($idA, $account['body']['impersonatorUserId']);
|
|
}
|
|
|
|
/**
|
|
* Test impersonation by phone header
|
|
*/
|
|
public function testImpersonateByPhone(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
$headers = array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders());
|
|
|
|
$userA = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'impersonate-by-phone-actor@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'Actor Phone',
|
|
]);
|
|
$this->assertEquals(201, $userA['headers']['status-code']);
|
|
$idA = $userA['body']['$id'];
|
|
|
|
$targetPhone = '+1555010200';
|
|
$userB = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'phone' => $targetPhone,
|
|
'name' => 'Target By Phone',
|
|
]);
|
|
$this->assertEquals(201, $userB['headers']['status-code']);
|
|
$idB = $userB['body']['$id'];
|
|
|
|
$this->client->call(Client::METHOD_PATCH, '/users/' . $idA . '/impersonator', $headers, ['impersonator' => true]);
|
|
$session = $this->client->call(Client::METHOD_POST, '/users/' . $idA . '/sessions', $headers);
|
|
$this->assertEquals(201, $session['headers']['status-code']);
|
|
|
|
$account = $this->client->call(Client::METHOD_GET, '/account', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-session' => $session['body']['secret'],
|
|
'x-appwrite-impersonate-user-phone' => $targetPhone,
|
|
]);
|
|
$this->assertEquals(200, $account['headers']['status-code']);
|
|
$this->assertEquals($idB, $account['body']['$id']);
|
|
$this->assertEquals($idA, $account['body']['impersonatorUserId']);
|
|
}
|
|
|
|
/**
|
|
* Test that user without impersonator capability does not get swapped when sending impersonation header
|
|
*/
|
|
public function testImpersonationRequiresCapability(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
$headers = array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders());
|
|
|
|
$userA = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'non-impersonator@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'Non Impersonator',
|
|
]);
|
|
$this->assertEquals(201, $userA['headers']['status-code']);
|
|
$idA = $userA['body']['$id'];
|
|
|
|
$userB = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'other-user@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'Other User',
|
|
]);
|
|
$this->assertEquals(201, $userB['headers']['status-code']);
|
|
$idB = $userB['body']['$id'];
|
|
|
|
$session = $this->client->call(Client::METHOD_POST, '/users/' . $idA . '/sessions', $headers);
|
|
$this->assertEquals(201, $session['headers']['status-code']);
|
|
|
|
$account = $this->client->call(Client::METHOD_GET, '/account', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-session' => $session['body']['secret'],
|
|
'x-appwrite-impersonate-user-id' => $idB,
|
|
]);
|
|
$this->assertEquals(200, $account['headers']['status-code']);
|
|
$this->assertEquals($idA, $account['body']['$id']);
|
|
$this->assertEquals('Non Impersonator', $account['body']['name']);
|
|
$this->assertArrayHasKey('impersonatorUserId', $account['body']);
|
|
$this->assertNull($account['body']['impersonatorUserId']);
|
|
}
|
|
|
|
/**
|
|
* Test that a normal user does not gain users.read while sending impersonation headers
|
|
*/
|
|
public function testImpersonationUsersReadScopeRequiresCapability(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
$headers = array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders());
|
|
|
|
$userA = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'non-impersonator-scope@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'Non Impersonator Scope',
|
|
]);
|
|
$this->assertEquals(201, $userA['headers']['status-code']);
|
|
$idA = $userA['body']['$id'];
|
|
|
|
$userB = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'non-impersonator-scope-target@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'Non Impersonator Scope Target',
|
|
]);
|
|
$this->assertEquals(201, $userB['headers']['status-code']);
|
|
|
|
$session = $this->client->call(Client::METHOD_POST, '/users/' . $idA . '/sessions', $headers);
|
|
$this->assertEquals(201, $session['headers']['status-code']);
|
|
|
|
$users = $this->client->call(Client::METHOD_GET, '/users', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-session' => $session['body']['secret'],
|
|
'x-appwrite-impersonate-user-id' => $userB['body']['$id'],
|
|
]);
|
|
$this->assertEquals(401, $users['headers']['status-code']);
|
|
$this->assertEquals('general_unauthorized_scope', $users['body']['type']);
|
|
}
|
|
|
|
/**
|
|
* Test that email and phone impersonation headers are ignored for users without impersonator capability
|
|
*/
|
|
public function testImpersonationByEmailAndPhoneRequireCapability(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
$headers = array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders());
|
|
|
|
$userA = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'non-impersonator-headers@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'Non Impersonator Headers',
|
|
]);
|
|
$this->assertEquals(201, $userA['headers']['status-code']);
|
|
$idA = $userA['body']['$id'];
|
|
|
|
$targetEmail = 'non-impersonator-target-email@appwrite.io';
|
|
$targetByEmail = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => $targetEmail,
|
|
'password' => 'password',
|
|
'name' => 'Target Email',
|
|
]);
|
|
$this->assertEquals(201, $targetByEmail['headers']['status-code']);
|
|
|
|
$targetPhone = '+1555010300';
|
|
$targetByPhone = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'phone' => $targetPhone,
|
|
'name' => 'Target Phone',
|
|
]);
|
|
$this->assertEquals(201, $targetByPhone['headers']['status-code']);
|
|
|
|
$session = $this->client->call(Client::METHOD_POST, '/users/' . $idA . '/sessions', $headers);
|
|
$this->assertEquals(201, $session['headers']['status-code']);
|
|
|
|
$accountByEmail = $this->client->call(Client::METHOD_GET, '/account', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-session' => $session['body']['secret'],
|
|
'x-appwrite-impersonate-user-email' => $targetEmail,
|
|
]);
|
|
$this->assertEquals(200, $accountByEmail['headers']['status-code']);
|
|
$this->assertEquals($idA, $accountByEmail['body']['$id']);
|
|
$this->assertEquals('Non Impersonator Headers', $accountByEmail['body']['name']);
|
|
$this->assertArrayHasKey('impersonatorUserId', $accountByEmail['body']);
|
|
$this->assertNull($accountByEmail['body']['impersonatorUserId']);
|
|
|
|
$accountByPhone = $this->client->call(Client::METHOD_GET, '/account', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-session' => $session['body']['secret'],
|
|
'x-appwrite-impersonate-user-phone' => $targetPhone,
|
|
]);
|
|
$this->assertEquals(200, $accountByPhone['headers']['status-code']);
|
|
$this->assertEquals($idA, $accountByPhone['body']['$id']);
|
|
$this->assertEquals('Non Impersonator Headers', $accountByPhone['body']['name']);
|
|
$this->assertArrayHasKey('impersonatorUserId', $accountByPhone['body']);
|
|
$this->assertNull($accountByPhone['body']['impersonatorUserId']);
|
|
}
|
|
|
|
/**
|
|
* Test that impersonator users get users.read before selecting a target
|
|
*/
|
|
public function testImpersonatorUsersReadScopeWithoutActiveImpersonation(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
$headers = array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders());
|
|
|
|
$user = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'impersonator-browse-users@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'Impersonator Browse Users',
|
|
]);
|
|
$this->assertEquals(201, $user['headers']['status-code']);
|
|
|
|
$patch = $this->client->call(
|
|
Client::METHOD_PATCH,
|
|
'/users/' . $user['body']['$id'] . '/impersonator',
|
|
$headers,
|
|
['impersonator' => true]
|
|
);
|
|
$this->assertEquals(200, $patch['headers']['status-code']);
|
|
|
|
$session = $this->client->call(Client::METHOD_POST, '/users/' . $user['body']['$id'] . '/sessions', $headers);
|
|
$this->assertEquals(201, $session['headers']['status-code']);
|
|
|
|
$users = $this->client->call(Client::METHOD_GET, '/users', [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-session' => $session['body']['secret'],
|
|
]);
|
|
$this->assertEquals(200, $users['headers']['status-code']);
|
|
$this->assertIsArray($users['body']['users']);
|
|
$this->assertGreaterThanOrEqual(1, count($users['body']['users']));
|
|
}
|
|
|
|
/**
|
|
* Test that when impersonating, users.read scope is granted and list users succeeds
|
|
*/
|
|
public function testImpersonationUsersReadScope(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
$headers = array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders());
|
|
|
|
$userA = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'impersonator-list-test@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'Impersonator List Test',
|
|
]);
|
|
$this->assertEquals(201, $userA['headers']['status-code']);
|
|
$idA = $userA['body']['$id'];
|
|
|
|
$userB = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'impersonator-list-target@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'List Target',
|
|
]);
|
|
$this->assertEquals(201, $userB['headers']['status-code']);
|
|
$idB = $userB['body']['$id'];
|
|
|
|
$this->client->call(Client::METHOD_PATCH, '/users/' . $idA . '/impersonator', $headers, ['impersonator' => true]);
|
|
$session = $this->client->call(Client::METHOD_POST, '/users/' . $idA . '/sessions', $headers);
|
|
$this->assertEquals(201, $session['headers']['status-code']);
|
|
|
|
$listHeaders = [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-session' => $session['body']['secret'],
|
|
'x-appwrite-impersonate-user-id' => $idB,
|
|
];
|
|
|
|
$users = $this->client->call(Client::METHOD_GET, '/users', $listHeaders);
|
|
$this->assertEquals(200, $users['headers']['status-code']);
|
|
$this->assertIsArray($users['body']['users']);
|
|
$this->assertGreaterThanOrEqual(2, count($users['body']['users']));
|
|
}
|
|
|
|
/**
|
|
* Test list users with query filter on impersonator attribute
|
|
*/
|
|
public function testListUsersFilterByImpersonator(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
$headers = array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders());
|
|
|
|
$userWithImpersonator = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => 'filter-impersonator-true@appwrite.io',
|
|
'password' => 'password',
|
|
'name' => 'Has Impersonator',
|
|
]);
|
|
$this->assertEquals(201, $userWithImpersonator['headers']['status-code']);
|
|
$this->client->call(Client::METHOD_PATCH, '/users/' . $userWithImpersonator['body']['$id'] . '/impersonator', $headers, ['impersonator' => true]);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', $headers, [
|
|
'queries' => [
|
|
Query::equal('impersonator', [true])->toString(),
|
|
],
|
|
]);
|
|
$this->assertEquals(200, $response['headers']['status-code']);
|
|
$this->assertIsArray($response['body']['users']);
|
|
$userIds = array_column($response['body']['users'], '$id');
|
|
$this->assertContains($userWithImpersonator['body']['$id'], $userIds);
|
|
|
|
$response = $this->client->call(Client::METHOD_GET, '/users', $headers, [
|
|
'queries' => [
|
|
Query::equal('impersonator', [false])->toString(),
|
|
],
|
|
]);
|
|
$this->assertEquals(200, $response['headers']['status-code']);
|
|
$this->assertIsArray($response['body']['users']);
|
|
}
|
|
|
|
/**
|
|
* Test impersonation via URL query params — mirrors the ?project= and ?devKey= pattern.
|
|
* Allows Console to embed impersonation in direct file/image URLs where headers cannot be set.
|
|
*/
|
|
public function testImpersonateByQueryParams(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
$headers = array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders());
|
|
|
|
$emailA = 'queryparam-impersonator-' . \uniqid() . '@appwrite.io';
|
|
$emailB = 'queryparam-target-' . \uniqid() . '@appwrite.io';
|
|
$emailC = 'queryparam-target-c-' . \uniqid() . '@appwrite.io';
|
|
$phone = '+1' . \rand(1000000000, 9999999999);
|
|
|
|
$userA = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => $emailA,
|
|
'password' => 'password',
|
|
'name' => 'Query Param Impersonator',
|
|
]);
|
|
$this->assertEquals(201, $userA['headers']['status-code']);
|
|
$idA = $userA['body']['$id'];
|
|
|
|
$userB = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => $emailB,
|
|
'password' => 'password',
|
|
'name' => 'Query Param Target',
|
|
]);
|
|
$this->assertEquals(201, $userB['headers']['status-code']);
|
|
$idB = $userB['body']['$id'];
|
|
|
|
$patch = $this->client->call(Client::METHOD_PATCH, '/users/' . $idA . '/impersonator', $headers, ['impersonator' => true]);
|
|
$this->assertEquals(200, $patch['headers']['status-code']);
|
|
|
|
$session = $this->client->call(Client::METHOD_POST, '/users/' . $idA . '/sessions', $headers);
|
|
$this->assertEquals(201, $session['headers']['status-code']);
|
|
$sessionSecret = $session['body']['secret'];
|
|
|
|
$sessionHeaders = [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-session' => $sessionSecret,
|
|
];
|
|
|
|
// Impersonate by user ID via query param
|
|
$account = $this->client->call(Client::METHOD_GET, '/account', $sessionHeaders, [
|
|
'impersonateUserId' => $idB,
|
|
]);
|
|
$this->assertEquals(200, $account['headers']['status-code']);
|
|
$this->assertEquals($idB, $account['body']['$id']);
|
|
$this->assertEquals('Query Param Target', $account['body']['name']);
|
|
$this->assertEquals($idA, $account['body']['impersonatorUserId']);
|
|
|
|
// Impersonate by email via query param
|
|
$accountByEmail = $this->client->call(Client::METHOD_GET, '/account', $sessionHeaders, [
|
|
'impersonateEmail' => $emailB,
|
|
]);
|
|
$this->assertEquals(200, $accountByEmail['headers']['status-code']);
|
|
$this->assertEquals($idB, $accountByEmail['body']['$id']);
|
|
$this->assertEquals($idA, $accountByEmail['body']['impersonatorUserId']);
|
|
|
|
// Impersonate by phone via query param (update target user with a phone first)
|
|
$this->client->call(Client::METHOD_PATCH, '/users/' . $idB . '/phone', $headers, [
|
|
'number' => $phone,
|
|
]);
|
|
$accountByPhone = $this->client->call(Client::METHOD_GET, '/account', $sessionHeaders, [
|
|
'impersonatePhone' => $phone,
|
|
]);
|
|
$this->assertEquals(200, $accountByPhone['headers']['status-code']);
|
|
$this->assertEquals($idB, $accountByPhone['body']['$id']);
|
|
$this->assertEquals($idA, $accountByPhone['body']['impersonatorUserId']);
|
|
|
|
// Header takes priority over query param when both are present
|
|
$userC = $this->client->call(Client::METHOD_POST, '/users', $headers, [
|
|
'userId' => ID::unique(),
|
|
'email' => $emailC,
|
|
'password' => 'password',
|
|
'name' => 'Query Param Target C',
|
|
]);
|
|
$this->assertEquals(201, $userC['headers']['status-code']);
|
|
$idC = $userC['body']['$id'];
|
|
|
|
$accountHeaderPriority = $this->client->call(
|
|
Client::METHOD_GET,
|
|
'/account',
|
|
array_merge($sessionHeaders, ['x-appwrite-impersonate-user-id' => $idC]),
|
|
['impersonateUserId' => $idB]
|
|
);
|
|
$this->assertEquals(200, $accountHeaderPriority['headers']['status-code']);
|
|
$this->assertEquals($idC, $accountHeaderPriority['body']['$id'], 'header must take priority over query param');
|
|
}
|
|
|
|
/**
|
|
* Test PATCH /users/:userId/impersonator for non-existent user returns 404
|
|
*/
|
|
public function testUpdateUserImpersonatorNotFound(): void
|
|
{
|
|
$projectId = $this->getProject()['$id'];
|
|
$headers = array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders());
|
|
|
|
$response = $this->client->call(Client::METHOD_PATCH, '/users/nonexistentuserid123/impersonator', $headers, [
|
|
'impersonator' => true,
|
|
]);
|
|
$this->assertEquals(404, $response['headers']['status-code']);
|
|
}
|
|
}
|