Merge branch '1.8.x' into chore-php-types

This commit is contained in:
Matej Bačo 2025-12-19 12:26:51 +01:00
commit 31b9ff4f05
18 changed files with 336 additions and 109 deletions

4
.env
View file

@ -69,8 +69,8 @@ _APP_STORAGE_ANTIVIRUS_PORT=3310
_APP_SMTP_HOST=maildev
_APP_SMTP_PORT=1025
_APP_SMTP_SECURE=
_APP_SMTP_USERNAME=
_APP_SMTP_PASSWORD=
_APP_SMTP_USERNAME=user
_APP_SMTP_PASSWORD=password
_APP_SMS_PROVIDER=sms://username:password@mock
_APP_SMS_FROM=+123456789
_APP_SMS_PROJECTS_DENY_LIST=

View file

@ -462,4 +462,15 @@ return [
'mock' => true,
'class' => 'Appwrite\\Auth\\OAuth2\\Mock',
],
'mock-unverified' => [
'name' => 'MockUnverified',
'developers' => 'https://appwrite.io',
'icon' => 'icon-appwrite',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => true,
'class' => 'Appwrite\\Auth\\OAuth2\\MockUnverified',
],
];

View file

@ -58,6 +58,8 @@ $admins = [
'projects.write',
'keys.read',
'keys.write',
'devKeys.read',
'devKeys.write',
'webhooks.read',
'webhooks.write',
'locale.read',

View file

@ -1639,9 +1639,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$failureRedirect(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.');
}
/**
* Is verified is not used yet, since we don't know after an account is created anymore if it was verified or not.
*/
$isVerified = $oauth2->isEmailVerified($accessToken);
$identity = $dbForProject->findOne('identities', [
@ -1653,16 +1650,32 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$user = $dbForProject->getDocument('users', $identity->getAttribute('userId'));
}
// If user is not found, check if there is an identity with the same provider user ID
// If user is not found, check if there is a user with the same email
if ($user === false || $user->isEmpty()) {
$userWithEmail = $dbForProject->findOne('users', [
Query::equal('email', [$email]),
]);
if (!$userWithEmail->isEmpty()) {
if (!$isVerified) {
$failureRedirect(Exception::GENERAL_BAD_REQUEST);
}
$user->setAttributes($userWithEmail->getArrayCopy());
}
}
// If user is not found, check if there is an identity with the same email
if ($user === false || $user->isEmpty()) {
$identityWithMatchingEmail = $dbForProject->findOne('identities', [
Query::equal('providerEmail', [$email]),
]);
if (!$identityWithMatchingEmail->isEmpty()) {
if (!$isVerified) {
$failureRedirect(Exception::GENERAL_BAD_REQUEST);
}
$user->setAttributes($dbForProject->getDocument('users', $identityWithMatchingEmail->getAttribute('userId'))->getArrayCopy());
}
}
if ($user === false || $user->isEmpty()) { // Last option -> create the user
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
@ -1674,14 +1687,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
}
}
// Makes sure this email is not already used in another identity
$identityWithMatchingEmail = $dbForProject->findOne('identities', [
Query::equal('providerEmail', [$email]),
]);
if (!$identityWithMatchingEmail->isEmpty()) {
$failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
}
try {
$emailCanonical = new Email($email);
} catch (Throwable) {
@ -1735,7 +1740,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'providerType' => MESSAGE_TYPE_EMAIL,
'identifier' => $email,
]));
} catch (Duplicate) {
$failureRedirect(Exception::USER_ALREADY_EXISTS);
}

View file

@ -294,7 +294,7 @@ App::post('/v1/projects')
// Hook allowing instant project mirroring during migration
// Outside of migration, hook is not registered and has no effect
$hooks->trigger('afterProjectCreation', [ $project, $pools, $cache ]);
$hooks->trigger('afterProjectCreation', [$project, $pools, $cache]);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@ -2079,6 +2079,7 @@ App::patch('/v1/projects/:projectId/smtp')
if ($enabled) {
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->SMTPAuth = (!empty($username) && !empty($password));
$mail->Username = $username;
$mail->Password = $password;
$mail->Host = $host;
@ -2094,7 +2095,7 @@ App::patch('/v1/projects/:projectId/smtp')
throw new Exception('Connection is not valid.');
}
} catch (Throwable $error) {
throw new Exception(Exception::PROJECT_SMTP_CONFIG_INVALID, 'Could not connect to SMTP server: ' . $error->getMessage());
throw new Exception(Exception::PROJECT_SMTP_CONFIG_INVALID, $error->getMessage());
}
}
@ -2665,7 +2666,7 @@ App::patch('/v1/projects/:projectId/auth/session-invalidation')
$auths = $project->getAttribute('auths', []);
$auths['invalidateSessions'] = $enabled;
$dbForPlatform->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths));
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});

View file

@ -1034,7 +1034,8 @@ App::init()
->inject('dbForPlatform')
->inject('queueForCertificates')
->inject('platform')
->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform) {
->inject('authorization')
->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform, Authorization $authorization) {
$hostname = $request->getHostname();
$cache = Config::getParam('hostnames', []);
$platformHostnames = $platform['hostnames'] ?? [];
@ -1065,7 +1066,7 @@ App::init()
}
// 4. Check/create rule (requires DB access)
Authorization::disable();
$authorization->disable();
try {
// TODO: (@Meldiron) Remove after 1.7.x migration
$isMd5 = System::getEnv('_APP_RULES_FORMAT') === 'md5';
@ -1121,7 +1122,7 @@ App::init()
} finally {
$cache[$domain->get()] = true;
Config::setParam('hostnames', $cache);
Authorization::reset();
$authorization->reset();
}
});

View file

@ -117,6 +117,28 @@ App::get('/v1/mock/tests/general/oauth2/user')
'id' => 1,
'name' => 'User Name',
'email' => 'useroauth@localhost.test',
'verified' => true,
]);
});
App::get('/v1/mock/tests/general/oauth2/user-unverified')
->desc('OAuth2 User Unverified')
->groups(['mock'])
->label('scope', 'public')
->label('docs', false)
->param('token', '', new Text(100), 'OAuth2 Access Token.')
->inject('response')
->action(function (string $token, Response $response) {
if ($token != '123456') {
throw new Exception(Exception::GENERAL_MOCK, 'Invalid token');
}
$response->json([
'id' => 2,
'name' => 'User Name Unverified',
'email' => 'useroauthunverified@localhost.test',
'verified' => false,
]);
});

View file

@ -1131,6 +1131,9 @@ services:
- "traefik.http.routers.appwrite_maildev_https.rule=Host(`mail.localhost`)"
- "traefik.http.routers.appwrite_maildev_https.service=appwrite_maildev"
- "traefik.http.routers.appwrite_maildev_https.tls=true"
environment:
- MAILDEV_INCOMING_USER=${_APP_SMTP_USERNAME}
- MAILDEV_INCOMING_PASS=${_APP_SMTP_PASSWORD}
request-catcher-webhook: # used mainly for dev tests (mock HTTP webhook)
image: appwrite/requestcatcher:1.0.0

View file

@ -130,7 +130,9 @@ class Mock extends OAuth2
*/
public function isEmailVerified(string $accessToken): bool
{
return true;
$user = $this->getUser($accessToken);
return $user['verified'] ?? true;
}
/**

View file

@ -0,0 +1,30 @@
<?php
namespace Appwrite\Auth\OAuth2;
class MockUnverified extends Mock
{
/**
* @return string
*/
public function getName(): string
{
return 'mock-unverified';
}
/**
* @param string $accessToken
*
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$user = $this->request('GET', 'http://localhost/' . $this->version . '/mock/tests/general/oauth2/user-unverified?token=' . \urlencode($accessToken));
$this->user = \json_decode($user, true);
}
return $this->user;
}
}

View file

@ -34,7 +34,7 @@ class Create extends Action
->setHttpPath('/v1/projects/:projectId/dev-keys')
->desc('Create dev key')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('scope', 'devKeys.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'devKeys',

View file

@ -28,7 +28,7 @@ class Delete extends Action
->setHttpPath('/v1/projects/:projectId/dev-keys/:keyId')
->desc('Delete dev key')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('scope', 'devKeys.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'devKeys',

View file

@ -28,7 +28,7 @@ class Get extends Action
->setHttpPath('/v1/projects/:projectId/dev-keys/:keyId')
->desc('Get dev key')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('scope', 'devKeys.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'devKeys',

View file

@ -29,7 +29,7 @@ class Update extends Action
->setHttpPath('/v1/projects/:projectId/dev-keys/:keyId')
->desc('Update dev key')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('scope', 'devKeys.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'devKeys',

View file

@ -32,7 +32,7 @@ class XList extends Action
->setHttpPath('/v1/projects/:projectId/dev-keys')
->desc('List dev keys')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('scope', 'devKeys.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'devKeys',

View file

@ -161,9 +161,9 @@ trait ProjectCustom
'senderEmail' => 'mailer@appwrite.io',
'senderName' => 'Mailer',
'host' => 'maildev',
'port' => 1025,
'username' => '',
'password' => '',
'port' => intval(System::getEnv('_APP_SMTP_PORT', "1025")),
'username' => System::getEnv('_APP_SMTP_USERNAME', 'user'),
'password' => System::getEnv('_APP_SMTP_PASSWORD', 'password'),
]);
$project = [

View file

@ -2183,6 +2183,157 @@ class AccountCustomClientTest extends Scope
$this->assertEquals('tuvwxyz', $response['body']['providerRefreshToken']);
$this->assertNotEquals($initialExpiry, $response['body']['providerAccessTokenExpiry']);
// Clean up - delete the user
$response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId, array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]));
$this->assertEquals(204, $response['headers']['status-code']);
return [];
}
public function testOAuthUnverifiedEmailCannotLinkToExistingAccount()
{
$provider = 'mock-unverified';
$appId = '1';
$secret = '123456';
// First, create a user with the same email that the unverified OAuth will try to use
$email = 'useroauthunverified@localhost.test';
$password = 'password';
$response = $this->client->call(Client::METHOD_POST, '/account', [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'userId' => ID::unique(),
'email' => $email,
'password' => $password,
]);
$this->assertEquals(201, $response['headers']['status-code']);
$existingUserId = $response['body']['$id'];
// Enable the mock-unverified provider
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/oauth2', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
]), [
'provider' => $provider,
'appId' => $appId,
'secret' => $secret,
'enabled' => true,
]);
$this->assertEquals(200, $response['headers']['status-code']);
// Attempt OAuth login with unverified email - should fail because existing user has same email
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'success' => 'http://localhost/v1/mock/tests/general/oauth2/success',
'failure' => 'http://localhost/v1/mock/tests/general/oauth2/failure',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$this->assertEquals('failure', $response['body']['result']);
// Clean up - delete the user
$response = $this->client->call(Client::METHOD_DELETE, '/users/' . $existingUserId, array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]));
$this->assertEquals(204, $response['headers']['status-code']);
return [];
}
public function testOAuthVerifiedEmailCanLinkToExistingAccount()
{
$provider = 'mock';
$appId = '1';
$secret = '123456';
$email = 'useroauth@localhost.test';
// Create a user with the same email that the verified OAuth will try to use
$response = $this->client->call(Client::METHOD_POST, '/account', [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'userId' => ID::unique(),
'email' => $email,
'password' => 'password',
]);
$this->assertEquals(201, $response['headers']['status-code']);
$existingUserId = $response['body']['$id'];
// Enable the mock provider
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/oauth2', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
]), [
'provider' => $provider,
'appId' => $appId,
'secret' => $secret,
'enabled' => true,
]);
$this->assertEquals(200, $response['headers']['status-code']);
// Attempt OAuth login with verified email - should succeed and link to existing account
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'success' => 'http://localhost/v1/mock/tests/general/oauth2/success',
'failure' => 'http://localhost/v1/mock/tests/general/oauth2/failure',
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('success', $response['body']['result']);
// Verify the OAuth identity was linked to the existing user
$sessionCookieKey = 'a_session_' . $this->getProject()['$id'];
$session = $response['cookies'][$sessionCookieKey];
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($existingUserId, $response['body']['$id']);
$this->assertEquals($email, $response['body']['email']);
// Clean up - delete the user
$response = $this->client->call(Client::METHOD_DELETE, '/users/' . $existingUserId, array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]));
$this->assertEquals(204, $response['headers']['status-code']);
return [];
}

View file

@ -23,9 +23,9 @@ class ProjectsConsoleClientTest extends Scope
use Async;
/**
* @group devKeys
* @group smtpAndTemplates
* @group projectsCRUD */
* @group projectsCRUD
*/
public function testCreateProject(): array
{
/**
@ -257,11 +257,11 @@ class ProjectsConsoleClientTest extends Scope
'search' => $id
]));
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertEquals($response['body']['total'], 3);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(4, $response['body']['total']);
$this->assertIsArray($response['body']['projects']);
$this->assertCount(3, $response['body']['projects']);
$this->assertEquals($response['body']['projects'][0]['name'], 'Project Test');
$this->assertCount(4, $response['body']['projects']);
$this->assertEquals('Project Test', $response['body']['projects'][0]['name']);
$response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
'content-type' => 'application/json',
@ -271,9 +271,9 @@ class ProjectsConsoleClientTest extends Scope
]));
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertEquals(3, $response['body']['total']);
$this->assertEquals(4, $response['body']['total']);
$this->assertIsArray($response['body']['projects']);
$this->assertCount(3, $response['body']['projects']);
$this->assertCount(4, $response['body']['projects']);
$this->assertEquals($response['body']['projects'][0]['$id'], $data['projectId']);
/**
@ -348,8 +348,8 @@ class ProjectsConsoleClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
$this->assertCount(1, $response['body']['projects']);
$this->assertEquals('Project Test 2', $response['body']['projects'][0]['name']);
$this->assertCount(2, $response['body']['projects']);
$this->assertEquals('Team 1 Project', $response['body']['projects'][0]['name']);
$response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
'content-type' => 'application/json',
@ -376,7 +376,7 @@ class ProjectsConsoleClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
$this->assertCount(4, $response['body']['projects']);
$this->assertCount(5, $response['body']['projects']);
$this->assertEquals('Project Test 2', $response['body']['projects'][0]['name']);
$this->assertEquals('Team 1 Project', $response['body']['projects'][1]['name']);
@ -387,9 +387,9 @@ class ProjectsConsoleClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
$this->assertCount(4, $response['body']['projects']);
$this->assertCount(5, $response['body']['projects']);
$this->assertEquals('Project Test', $response['body']['projects'][0]['name']);
$this->assertEquals('Team 1 Project', $response['body']['projects'][2]['name']);
$this->assertEquals('Original Project', $response['body']['projects'][2]['name']);
$response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
'content-type' => 'application/json',
@ -402,8 +402,8 @@ class ProjectsConsoleClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
$this->assertCount(3, $response['body']['projects']);
$this->assertEquals('Team 1 Project', $response['body']['projects'][1]['name']);
$this->assertCount(4, $response['body']['projects']);
$this->assertEquals('Original Project', $response['body']['projects'][1]['name']);
$response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
'content-type' => 'application/json',
@ -618,6 +618,14 @@ class ProjectsConsoleClientTest extends Scope
public function testUpdateProjectSMTP($data): array
{
$id = $data['projectId'];
$smtpHost = System::getEnv('_APP_SMTP_HOST', "maildev");
$smtpPort = intval(System::getEnv('_APP_SMTP_PORT', "1025"));
$smtpUsername = System::getEnv('_APP_SMTP_USERNAME', 'user');
$smtpPassword = System::getEnv('_APP_SMTP_PASSWORD', 'password');
/**
* Test for SUCCESS: Valid Credentials
*/
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/smtp', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@ -625,23 +633,23 @@ class ProjectsConsoleClientTest extends Scope
'enabled' => true,
'senderEmail' => 'mailer@appwrite.io',
'senderName' => 'Mailer',
'host' => 'maildev',
'port' => 1025,
'username' => 'user',
'password' => 'password',
'host' => $smtpHost,
'port' => $smtpPort,
'username' => $smtpUsername,
'password' => $smtpPassword,
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertTrue($response['body']['smtpEnabled']);
$this->assertEquals('mailer@appwrite.io', $response['body']['smtpSenderEmail']);
$this->assertEquals('Mailer', $response['body']['smtpSenderName']);
$this->assertEquals('maildev', $response['body']['smtpHost']);
$this->assertEquals(1025, $response['body']['smtpPort']);
$this->assertEquals('user', $response['body']['smtpUsername']);
$this->assertEquals('password', $response['body']['smtpPassword']);
$this->assertEquals($smtpHost, $response['body']['smtpHost']);
$this->assertEquals($smtpPort, $response['body']['smtpPort']);
$this->assertEquals($smtpUsername, $response['body']['smtpUsername']);
$this->assertEquals($smtpPassword, $response['body']['smtpPassword']);
$this->assertEquals('', $response['body']['smtpSecure']);
/** Test Reading Project */
// Check the project
$response = $this->client->call(Client::METHOD_GET, '/projects/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@ -651,12 +659,32 @@ class ProjectsConsoleClientTest extends Scope
$this->assertTrue($response['body']['smtpEnabled']);
$this->assertEquals('mailer@appwrite.io', $response['body']['smtpSenderEmail']);
$this->assertEquals('Mailer', $response['body']['smtpSenderName']);
$this->assertEquals('maildev', $response['body']['smtpHost']);
$this->assertEquals(1025, $response['body']['smtpPort']);
$this->assertEquals('user', $response['body']['smtpUsername']);
$this->assertEquals('password', $response['body']['smtpPassword']);
$this->assertEquals($smtpHost, $response['body']['smtpHost']);
$this->assertEquals($smtpPort, $response['body']['smtpPort']);
$this->assertEquals($smtpUsername, $response['body']['smtpUsername']);
$this->assertEquals($smtpPassword, $response['body']['smtpPassword']);
$this->assertEquals('', $response['body']['smtpSecure']);
/**
* Test for FAILURE: Invalid Credentials
*/
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/smtp', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'enabled' => true,
'senderEmail' => 'fail@appwrite.io',
'senderName' => 'Failing Mailer',
'host' => $smtpHost,
'port' => $smtpPort,
'username' => 'invalid-user',
'password' => 'bad-password',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$this->assertEquals(Exception::PROJECT_SMTP_CONFIG_INVALID, $response['body']['type']);
$this->assertStringContainsStringIgnoringCase('Could not authenticate', $response['body']['message']);
return $data;
}
@ -665,6 +693,11 @@ class ProjectsConsoleClientTest extends Scope
*/
public function testCreateProjectSMTPTests(): void
{
$smtpHost = System::getEnv('_APP_SMTP_HOST', "maildev");
$smtpPort = intval(System::getEnv('_APP_SMTP_PORT', "1025"));
$smtpUsername = System::getEnv('_APP_SMTP_USERNAME', 'user');
$smtpPassword = System::getEnv('_APP_SMTP_PASSWORD', 'password');
// Create a team
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
'content-type' => 'application/json',
@ -699,10 +732,10 @@ class ProjectsConsoleClientTest extends Scope
'senderEmail' => 'custommailer@appwrite.io',
'senderName' => 'Custom Mailer',
'replyTo' => 'reply@appwrite.io',
'host' => 'maildev',
'port' => 1025,
'username' => '',
'password' => '',
'host' => $smtpHost,
'port' => $smtpPort,
'username' => $smtpUsername,
'password' => $smtpPassword,
]);
$this->assertEquals(204, $response['headers']['status-code']);
@ -736,10 +769,10 @@ class ProjectsConsoleClientTest extends Scope
'senderEmail' => 'custommailer@appwrite.io',
'senderName' => 'Custom Mailer',
'replyTo' => 'reply@appwrite.io',
'host' => 'maildev',
'port' => 1025,
'username' => '',
'password' => '',
'host' => $smtpHost,
'port' => $smtpPort,
'username' => $smtpUsername,
'password' => $smtpPassword,
]);
$this->assertEquals(204, $response['headers']['status-code']);
@ -752,10 +785,10 @@ class ProjectsConsoleClientTest extends Scope
'senderEmail' => 'custommailer@appwrite.io',
'senderName' => 'Custom Mailer',
'replyTo' => 'reply@appwrite.io',
'host' => 'maildev',
'port' => 1025,
'username' => '',
'password' => '',
'host' => $smtpHost,
'port' => $smtpPort,
'username' => $smtpUsername,
'password' => $smtpPassword,
]);
$this->assertEquals(400, $response['headers']['status-code']);
@ -776,7 +809,7 @@ class ProjectsConsoleClientTest extends Scope
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('Account Verification', $response['body']['subject']);
$this->assertEquals('Account Verification for {{project}}', $response['body']['subject']);
$this->assertEquals('', $response['body']['senderEmail']);
$this->assertEquals('verification', $response['body']['type']);
$this->assertEquals('en-us', $response['body']['locale']);
@ -879,7 +912,7 @@ class ProjectsConsoleClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals('Project Test 2', $response['body']['name']);
$this->assertEquals('Project Test', $response['body']['name']);
$this->assertArrayHasKey('platforms', $response['body']);
$this->assertArrayHasKey('webhooks', $response['body']);
$this->assertArrayHasKey('keys', $response['body']);
@ -959,39 +992,6 @@ class ProjectsConsoleClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(15, $response['body']['authDuration']);
// Create session
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
]), [
'email' => $userEmail,
'password' => 'password',
]);
$this->assertEquals(201, $response['headers']['status-code']);
$sessionCookie = $response['headers']['set-cookie'];
// Wait 10 seconds, ensure valid session, extend session
\sleep(10);
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'Cookie' => $sessionCookie,
]));
$this->assertEquals(200, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_PATCH, '/account/sessions/current', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'cookie' => $sessionCookie,
]));
$this->assertEquals(200, $response['headers']['status-code']);
// Wait 20 seconds, ensure non-valid session
\sleep(20);
@ -3132,7 +3132,7 @@ class ProjectsConsoleClientTest extends Scope
$this->assertContains('users.write', $response['body']['scopes']);
$this->assertContains('collections.read', $response['body']['scopes']);
$this->assertContains('tables.read', $response['body']['scopes']);
$this->assertCount(3, $response['body']['scopes']);
$this->assertCount(4, $response['body']['scopes']);
$this->assertArrayHasKey('sdks', $response['body']);
$this->assertEmpty($response['body']['sdks']);
$this->assertArrayHasKey('accessedAt', $response['body']);
@ -3151,7 +3151,7 @@ class ProjectsConsoleClientTest extends Scope
$this->assertContains('users.write', $response['body']['scopes']);
$this->assertContains('collections.read', $response['body']['scopes']);
$this->assertContains('tables.read', $response['body']['scopes']);
$this->assertCount(3, $response['body']['scopes']);
$this->assertCount(4, $response['body']['scopes']);
$this->assertArrayHasKey('sdks', $response['body']);
$this->assertEmpty($response['body']['sdks']);
$this->assertArrayHasKey('accessedAt', $response['body']);
@ -5086,8 +5086,8 @@ class ProjectsConsoleClientTest extends Scope
$this->assertEmpty($response['body']);
/**
* Get rate limit trying to use the deleted key
*/
* Get rate limit trying to use the deleted key
*/
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,