From f5f074eb862bb1d92f26b3aaf172c4776c0172ae Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sat, 11 Jan 2020 15:58:02 +0200 Subject: [PATCH] Tests - work in progress --- CHANGES.md | 6 +- app/app.php | 12 +- .../templates/en.email.auth.recovery.tpl | 2 +- app/controllers/api/account.php | 316 +++--- app/controllers/api/teams.php | 4 +- app/init.php | 7 +- docker-compose.yml | 35 +- tests/e2e/AccountBase.php | 920 +++++++++++++++++ tests/e2e/AccountConsoleClientTest.php | 14 + tests/e2e/AccountCustomClientTest.php | 14 + tests/e2e/AccountCustomServerTest.php | 9 + tests/e2e/Client.php | 11 +- tests/e2e/Scopes/ProjectConsole.php | 15 + tests/e2e/Scopes/ProjectCustom.php | 119 +++ tests/e2e/Scopes/Scope.php | 55 ++ tests/e2e/Scopes/SideClient.php | 11 + tests/e2e/Scopes/SideServer.php | 19 + tests/old/AccountTest.php | 930 ++++++++++++++++++ tests/old/Base.php | 41 + tests/{e2e => old}/BaseConsole.php | 0 tests/{e2e => old}/BaseProjects.php | 0 tests/{e2e => old}/ConsoleHealthTest.php | 0 tests/{e2e => old}/ConsoleProjectsTest.php | 0 tests/{e2e => old}/ConsoleTest.php | 0 tests/{e2e => old}/ProjectAvatarsTest.php | 0 tests/{e2e => old}/ProjectDatabaseTest.php | 0 tests/{e2e => old}/ProjectLocaleTest.php | 0 tests/{e2e => old}/ProjectStorageTest.php | 0 tests/{e2e => old}/ProjectUsersTest.php | 0 29 files changed, 2381 insertions(+), 159 deletions(-) create mode 100644 tests/e2e/AccountBase.php create mode 100644 tests/e2e/AccountConsoleClientTest.php create mode 100644 tests/e2e/AccountCustomClientTest.php create mode 100644 tests/e2e/AccountCustomServerTest.php create mode 100644 tests/e2e/Scopes/ProjectConsole.php create mode 100644 tests/e2e/Scopes/ProjectCustom.php create mode 100644 tests/e2e/Scopes/Scope.php create mode 100644 tests/e2e/Scopes/SideClient.php create mode 100644 tests/e2e/Scopes/SideServer.php create mode 100644 tests/old/AccountTest.php create mode 100644 tests/old/Base.php rename tests/{e2e => old}/BaseConsole.php (100%) rename tests/{e2e => old}/BaseProjects.php (100%) rename tests/{e2e => old}/ConsoleHealthTest.php (100%) rename tests/{e2e => old}/ConsoleProjectsTest.php (100%) rename tests/{e2e => old}/ConsoleTest.php (100%) rename tests/{e2e => old}/ProjectAvatarsTest.php (100%) rename tests/{e2e => old}/ProjectDatabaseTest.php (100%) rename tests/{e2e => old}/ProjectLocaleTest.php (100%) rename tests/{e2e => old}/ProjectStorageTest.php (100%) rename tests/{e2e => old}/ProjectUsersTest.php (100%) diff --git a/CHANGES.md b/CHANGES.md index e1bedb512..f6d3acbff 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,11 +4,11 @@ * Upgraded core API PHP version to 7.4 * New database rule validation options -* Update docs example with auth info * Allow non-web platform skip origin header -* Limited to console UI to show max 5 alerts at the same time +* Limited console dashboard to show max 5 alerts at the same time * Added new webhooks events -* Normnailized all webhooks event names +* Normailized all webhooks event names +* Merged Auth and Account service for making the API more REST compatible ## Bug Fixes diff --git a/app/app.php b/app/app.php index b73ab3cf5..a1afaedc1 100644 --- a/app/app.php +++ b/app/app.php @@ -211,7 +211,7 @@ $utopia->shutdown(function () use ($response, $request, $webhook, $audit, $usage if (!empty($webhook->getParam('event'))) { $webhook->trigger(); } - + if (!empty($audit->getParam('event'))) { $audit->trigger(); } @@ -361,7 +361,7 @@ $utopia->get('/humans.txt') ); $utopia->get('/v1/info') // This is only visible to gods -->label('scope', 'god') + ->label('scope', 'god') ->label('docs', false) ->action( function () use ($response, $user, $project, $version, $env) { //TODO CONSIDER BLOCKING THIS ACTION TO ROLE GOD @@ -410,7 +410,7 @@ $utopia->get('/v1/proxy') } ); - $utopia->get('/v1/open-api-2.json') +$utopia->get('/v1/open-api-2.json') ->label('scope', 'public') ->label('docs', false) ->param('platform', 'client', function () {return new WhiteList(['client', 'server']);}, 'Choose target platform.', true) @@ -740,6 +740,7 @@ $utopia->get('/v1/debug') ->action( function () use ($response, $request, $utopia, $domain, $services) { $output = [ + 'scopes' => [], 'webhooks' => [], 'methods' => [], 'routes' => [], @@ -767,6 +768,10 @@ $utopia->get('/v1/debug') continue; } + if ($route->getLabel('scope', false)) { + $output['scopes'][$route->getLabel('scope', false)] = $route->getMethod().' '.$route->getURL(); + } + if ($route->getLabel('sdk.description', false)) { if(!realpath(__DIR__.'/../'.$route->getLabel('sdk.description', false))) { throw new Exception('Docs file ('.$route->getLabel('sdk.description', false).') is missing', 500); @@ -802,6 +807,7 @@ $utopia->get('/v1/debug') } } + ksort($output['scopes']); ksort($output['webhooks']); ksort($output['methods']); ksort($output['routes']); diff --git a/app/config/locales/templates/en.email.auth.recovery.tpl b/app/config/locales/templates/en.email.auth.recovery.tpl index 7a6fa2fa6..82b365546 100644 --- a/app/config/locales/templates/en.email.auth.recovery.tpl +++ b/app/config/locales/templates/en.email.auth.recovery.tpl @@ -15,7 +15,7 @@ {{redirect}}

- If you didn’t ask to reset your password, you can ignore this message. + If you didn't ask to reset your password, you can ignore this message.

Thanks, diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 733f70c92..850b24a1d 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1,8 +1,10 @@ init(function() use ($providers, &$oauthKeys) { + foreach ($providers as $key => $provider) { + if (!$provider['enabled']) { + continue; + } + + $oauthKeys[] = 'oauth'.ucfirst($key); + $oauthKeys[] = 'oauth'.ucfirst($key).'AccessToken'; + } + +}); + $utopia->get('/v1/account') ->desc('Get Account') ->label('scope', 'account') @@ -31,18 +47,7 @@ $utopia->get('/v1/account') ->label('sdk.method', 'getAccount') ->label('sdk.description', '/docs/references/account/get.md') ->action( - function () use ($response, &$user, $providers) { - $oauthKeys = []; - - foreach ($providers as $key => $provider) { - if (!$provider['enabled']) { - continue; - } - - $oauthKeys[] = 'oauth'.ucfirst($key); - $oauthKeys[] = 'oauth'.ucfirst($key).'AccessToken'; - } - + function () use ($response, &$user, $oauthKeys) { $response->json(array_merge($user->getArrayCopy(array_merge( [ '$uid', @@ -109,7 +114,7 @@ $utopia->get('/v1/account/sessions') $dd->parse(); $sessions[$index] = [ - 'id' => $token->getUid(), + '$uid' => $token->getUid(), 'OS' => $dd->getOs(), 'client' => $dd->getClient(), 'device' => $dd->getDevice(), @@ -146,6 +151,7 @@ $utopia->get('/v1/account/logs') function () use ($response, $register, $project, $user) { $adapter = new AuditAdapter($register->get('db')); $adapter->setNamespace('app_'.$project->getUid()); + $audit = new Audit($adapter); $countries = Locale::getText('countries'); @@ -220,7 +226,7 @@ $utopia->post('/v1/account') ->param('password', '', function () { return new Password(); }, 'User password') ->param('name', '', function () { return new Text(100); }, 'User name', true) ->action( - function ($email, $password, $name) use ($request, $response, $providers, $audit, $projectDB, $project, $webhook) { + function ($email, $password, $name) use ($register, $request, $response, $audit, $projectDB, $project, $webhook, $oauthKeys) { if ('console' === $project->getUid()) { $whitlistEmails = $project->getAttribute('authWhitelistEmails'); $whitlistIPs = $project->getAttribute('authWhitelistIPs'); @@ -276,14 +282,6 @@ $utopia->post('/v1/account') throw new Exception('Failed saving user to DB', 500); } - Authorization::setRole('user:'.$user->getUid()); - - $user = $projectDB->createDocument($user->getArrayCopy()); - - if (false === $user) { - throw new Exception('Failed saving tokens to DB', 500); - } - $webhook ->setParam('payload', [ 'name' => $name, @@ -297,26 +295,17 @@ $utopia->post('/v1/account') ->setParam('resource', 'users/'.$user->getUid()) ; - $oauthKeys = []; - - foreach ($providers as $key => $provider) { - if (!$provider['enabled']) { - continue; - } - - $oauthKeys[] = 'oauth'.ucfirst($key); - $oauthKeys[] = 'oauth'.ucfirst($key).'AccessToken'; - } - - $response->json(array_merge($user->getArrayCopy(array_merge( - [ - '$uid', - 'email', - 'registration', - 'name', - ], - $oauthKeys - )), ['roles' => Authorization::getRoles()])); + $response + ->setStatusCode(Response::STATUS_CODE_CREATED) + ->json(array_merge($user->getArrayCopy(array_merge( + [ + '$uid', + 'email', + 'registration', + 'name', + ], + $oauthKeys + )), ['roles' => Authorization::getRoles()])); } ); @@ -342,11 +331,11 @@ $utopia->post('/v1/account/sessions') ], ]); - if (!$profile || !Auth::passwordVerify($password, $profile->getAttribute('password'))) { + if (false == $profile || !Auth::passwordVerify($password, $profile->getAttribute('password'))) { $audit //->setParam('userId', $profile->getUid()) ->setParam('event', 'account.sesssions.failed') - ->setParam('resource', 'users/'.$profile->getUid()) + ->setParam('resource', 'users/'.($profile ? $profile->getUid() : '')) ; throw new Exception('Invalid credentials', 401); // Wrong password or username @@ -354,8 +343,7 @@ $utopia->post('/v1/account/sessions') $expiry = time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; $secret = Auth::tokenGenerator(); - - $profile->setAttribute('tokens', new Document([ + $session = new Document([ '$collection' => Database::SYSTEM_COLLECTION_TOKENS, '$permissions' => ['read' => ['user:'.$profile->getUid()], 'write' => ['user:'.$profile->getUid()]], 'type' => Auth::TOKEN_TYPE_LOGIN, @@ -363,10 +351,18 @@ $utopia->post('/v1/account/sessions') 'expire' => $expiry, 'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'), 'ip' => $request->getIP(), - ]), Document::SET_TYPE_APPEND); + ]); Authorization::setRole('user:'.$profile->getUid()); + $session = $projectDB->createDocument($session->getArrayCopy()); + + if (false === $session) { + throw new Exception('Failed saving session to DB', 500); + } + + $profile->setAttribute('tokens', $session, Document::SET_TYPE_APPEND); + $profile = $projectDB->updateDocument($profile->getArrayCopy()); if (false === $profile) { @@ -382,15 +378,15 @@ $utopia->post('/v1/account/sessions') $audit ->setParam('userId', $profile->getUid()) - ->setParam('event', 'account.sesssions.create') + ->setParam('event', 'account.sessions.create') ->setParam('resource', 'users/'.$profile->getUid()) ; - + $response - ->addCookie(Auth::$cookieName, Auth::encodeSession($profile->getUid(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE); - - $response - ->json(array('result' => 'success')); + ->addCookie(Auth::$cookieName, Auth::encodeSession($profile->getUid(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE) + ->setStatusCode(Response::STATUS_CODE_CREATED) + ->json($session->getArrayCopy(['$uid', 'type', 'expire'])) + ; } ); @@ -583,20 +579,21 @@ $utopia->get('/v1/account/sessions/oauth/:provider/redirect') $secret = Auth::tokenGenerator(); $expiry = time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $session = new Document([ + '$collection' => Database::SYSTEM_COLLECTION_TOKENS, + '$permissions' => ['read' => ['user:'.$user['$uid']], 'write' => ['user:'.$user['$uid']]], + 'type' => Auth::TOKEN_TYPE_LOGIN, + 'secret' => Auth::hash($secret), // On way hash encryption to protect DB leak + 'expire' => $expiry, + 'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'), + 'ip' => $request->getIP(), + ]); $user ->setAttribute('oauth'.ucfirst($provider), $oauthID) ->setAttribute('oauth'.ucfirst($provider).'AccessToken', $accessToken) ->setAttribute('status', Auth::USER_STATUS_ACTIVATED) - ->setAttribute('tokens', new Document([ - '$collection' => Database::SYSTEM_COLLECTION_TOKENS, - '$permissions' => ['read' => ['user:'.$user['$uid']], 'write' => ['user:'.$user['$uid']]], - 'type' => Auth::TOKEN_TYPE_LOGIN, - 'secret' => Auth::hash($secret), // On way hash encryption to protect DB leak - 'expire' => $expiry, - 'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'), - 'ip' => $request->getIP(), - ]), Document::SET_TYPE_APPEND) + ->setAttribute('tokens', $session, Document::SET_TYPE_APPEND) ; Authorization::setRole('user:'.$user->getUid()); @@ -631,7 +628,7 @@ $utopia->patch('/v1/account/name') ->label('sdk.description', '/docs/references/account/update-name.md') ->param('name', '', function () { return new Text(100); }, 'User name') ->action( - function ($name) use ($response, $user, $projectDB, $audit) { + function ($name) use ($response, $user, $projectDB, $audit, $oauthKeys) { $user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [ 'name' => $name, ])); @@ -645,7 +642,15 @@ $utopia->patch('/v1/account/name') ->setParam('resource', 'users/'.$user->getUid()) ; - $response->json(array('result' => 'success')); + $response->json(array_merge($user->getArrayCopy(array_merge( + [ + '$uid', + 'email', + 'registration', + 'name', + ], + $oauthKeys + )), ['roles' => Authorization::getRoles()])); } ); @@ -659,7 +664,7 @@ $utopia->patch('/v1/account/password') ->param('password', '', function () { return new Password(); }, 'New password') ->param('old-password', '', function () { return new Password(); }, 'Old password') ->action( - function ($password, $oldPassword) use ($response, $user, $projectDB, $audit) { + function ($password, $oldPassword) use ($response, $user, $projectDB, $audit, $oauthKeys) { if (!Auth::passwordVerify($oldPassword, $user->getAttribute('password'))) { // Double check user password throw new Exception('Invalid credentials', 401); } @@ -677,7 +682,15 @@ $utopia->patch('/v1/account/password') ->setParam('resource', 'users/'.$user->getUid()) ; - $response->json(array('result' => 'success')); + $response->json(array_merge($user->getArrayCopy(array_merge( + [ + '$uid', + 'email', + 'registration', + 'name', + ], + $oauthKeys + )), ['roles' => Authorization::getRoles()])); } ); @@ -691,7 +704,7 @@ $utopia->patch('/v1/account/email') ->param('email', '', function () { return new Email(); }, 'Email Address') ->param('password', '', function () { return new Password(); }, 'User Password') ->action( - function ($email, $password) use ($response, $user, $projectDB, $audit) { + function ($email, $password) use ($response, $user, $projectDB, $audit, $oauthKeys) { if (!Auth::passwordVerify($password, $user->getAttribute('password'))) { // Double check user password throw new Exception('Invalid credentials', 401); } @@ -724,7 +737,15 @@ $utopia->patch('/v1/account/email') ->setParam('resource', 'users/'.$user->getUid()) ; - $response->json(array('result' => 'success')); + $response->json(array_merge($user->getArrayCopy(array_merge( + [ + '$uid', + 'email', + 'registration', + 'name', + ], + $oauthKeys + )), ['roles' => Authorization::getRoles()])); } ); @@ -751,7 +772,19 @@ $utopia->patch('/v1/account/prefs') ->setParam('resource', 'users/'.$user->getUid()) ; - $response->json(array('result' => 'success')); + $prefs = $user->getAttribute('prefs', '{}'); + + if (empty($prefs)) { + $prefs = '[]'; + } + + try { + $prefs = json_decode($prefs, true); + } catch (\Exception $error) { + throw new Exception('Failed to parse preferences', 500); + } + + $response->json($prefs); } ); @@ -795,39 +828,46 @@ $utopia->delete('/v1/account') $response ->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE) - ->json(array('result' => 'success')); + ->noContent() + ; } ); -$utopia->delete('/v1/account/sessions/current') - ->desc('Delete Current Account Session') - ->label('webhook', 'account.sessions.delete') +$utopia->delete('/v1/account/sessions') + ->desc('Delete All Account Sessions') ->label('scope', 'account') + ->label('webhook', 'account.sessions.delete') ->label('sdk.namespace', 'account') - ->label('sdk.method', 'deleteAccountCurrentSession') - ->label('sdk.description', '/docs/references/account/delete-session-current.md') + ->label('sdk.method', 'deleteAccountSessions') + ->label('sdk.description', '/docs/references/account/delete-sessions.md') ->label('abuse-limit', 100) ->action( function () use ($response, $request, $user, $projectDB, $audit, $webhook) { - $token = Auth::tokenVerify($user->getAttribute('tokens'), Auth::TOKEN_TYPE_LOGIN, Auth::$secret); + $tokens = $user->getAttribute('tokens', []); - if (!$projectDB->deleteDocument($token)) { - throw new Exception('Failed to remove token from DB', 500); + foreach ($tokens as $token) { /* @var $token Document */ + if (!$projectDB->deleteDocument($token->getUid())) { + throw new Exception('Failed to remove token from DB', 500); + } + + $audit + ->setParam('event', 'account.sessions.delete') + ->setParam('resource', '/user/'.$user->getUid()) + ; + + $webhook + ->setParam('payload', [ + 'name' => $user->getAttribute('name', ''), + 'email' => $user->getAttribute('email', ''), + ]) + ; + + if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + $response->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE); + } } - $webhook - ->setParam('payload', [ - 'name' => $user->getAttribute('name', ''), - 'email' => $user->getAttribute('email', ''), - ]) - ; - - $audit->setParam('event', 'account.sessions.delete'); - - $response - ->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE) - ->json(array('result' => 'success')) - ; + $response->noContent(); } ); @@ -868,45 +908,39 @@ $utopia->delete('/v1/account/sessions/:id') } } - $response->json(array('result' => 'success')); + $response->noContent(); } ); -$utopia->delete('/v1/account/sessions') - ->desc('Delete All Account Sessions') - ->label('scope', 'account') +$utopia->delete('/v1/account/sessions/current') + ->desc('Delete Current Account Session') ->label('webhook', 'account.sessions.delete') + ->label('scope', 'account') ->label('sdk.namespace', 'account') - ->label('sdk.method', 'deleteAccountSessions') - ->label('sdk.description', '/docs/references/account/delete-sessions.md') + ->label('sdk.method', 'deleteAccountCurrentSession') + ->label('sdk.description', '/docs/references/account/delete-session-current.md') ->label('abuse-limit', 100) ->action( function () use ($response, $request, $user, $projectDB, $audit, $webhook) { - $tokens = $user->getAttribute('tokens', []); + $token = Auth::tokenVerify($user->getAttribute('tokens'), Auth::TOKEN_TYPE_LOGIN, Auth::$secret); - foreach ($tokens as $token) { /* @var $token Document */ - if (!$projectDB->deleteDocument($token->getUid())) { - throw new Exception('Failed to remove token from DB', 500); - } - - $audit - ->setParam('event', 'account.sessions.delete') - ->setParam('resource', '/user/'.$user->getUid()) - ; - - $webhook - ->setParam('payload', [ - 'name' => $user->getAttribute('name', ''), - 'email' => $user->getAttribute('email', ''), - ]) - ; - - if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too - $response->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE); - } + if (!$projectDB->deleteDocument($token)) { + throw new Exception('Failed to remove token from DB', 500); } - $response->json(array('result' => 'success')); + $webhook + ->setParam('payload', [ + 'name' => $user->getAttribute('name', ''), + 'email' => $user->getAttribute('email', ''), + ]) + ; + + $audit->setParam('event', 'account.sessions.delete'); + + $response + ->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE) + ->noContent() + ; } ); @@ -936,8 +970,7 @@ $utopia->post('/v1/account/recovery') } $secret = Auth::tokenGenerator(); - - $profile->setAttribute('tokens', new Document([ + $recovery = new Document([ '$collection' => Database::SYSTEM_COLLECTION_TOKENS, '$permissions' => ['read' => ['user:'.$profile->getUid()], 'write' => ['user:'.$profile->getUid()]], 'type' => Auth::TOKEN_TYPE_RECOVERY, @@ -945,10 +978,18 @@ $utopia->post('/v1/account/recovery') 'expire' => time() + Auth::TOKEN_EXPIRATION_RECOVERY, 'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'), 'ip' => $request->getIP(), - ]), Document::SET_TYPE_APPEND); - + ]); + Authorization::setRole('user:'.$profile->getUid()); + $recovery = $projectDB->createDocument($recovery->getArrayCopy()); + + if (false === $recovery) { + throw new Exception('Failed saving recovery to DB', 500); + } + + $profile->setAttribute('tokens', $recovery, Document::SET_TYPE_APPEND); + $profile = $projectDB->updateDocument($profile->getArrayCopy()); if (false === $profile) { @@ -978,7 +1019,7 @@ $utopia->post('/v1/account/recovery') try { $mail->send(); } catch (\Exception $error) { - //throw new Exception('Problem sending mail: ' . $error->getMessage(), 500); + throw new Exception('Error sending mail: ' . $error->getMessage(), 500); } $audit @@ -986,7 +1027,10 @@ $utopia->post('/v1/account/recovery') ->setParam('event', 'account.recovery.create') ; - $response->json(array('result' => 'success')); + $response + ->setStatusCode(Response::STATUS_CODE_CREATED) + ->json($recovery->getArrayCopy(['$uid', 'type', 'expire'])) + ; } ); @@ -998,7 +1042,7 @@ $utopia->put('/v1/account/recovery') ->label('sdk.description', '/docs/references/account/update-recovery.md') ->label('abuse-limit', 10) ->label('abuse-key', 'url:{url},userId:{param-userId}') - ->param('userId', '', function () { return new UID(); }, 'User account email address.') + ->param('userId', '', function () { return new UID(); }, 'User account UID address.') ->param('token', '', function () { return new Text(256); }, 'Valid reset token.') ->param('password-a', '', function () { return new Password(); }, 'New password.') ->param('password-b', '', function () {return new Password(); }, 'New password again.') @@ -1021,10 +1065,10 @@ $utopia->put('/v1/account/recovery') throw new Exception('User not found', 404); // TODO maybe hide this } - $token = Auth::tokenVerify($profile->getAttribute('tokens', []), Auth::TOKEN_TYPE_RECOVERY, $token); + $recovery = Auth::tokenVerify($profile->getAttribute('tokens', []), Auth::TOKEN_TYPE_RECOVERY, $token); - if (!$token) { - throw new Exception('Recovery token is not valid', 401); + if (!$recovery) { + throw new Exception('Invalid recovery token', 401); } Authorization::setRole('user:'.$profile->getUid()); @@ -1039,8 +1083,12 @@ $utopia->put('/v1/account/recovery') throw new Exception('Failed saving user to DB', 500); } - if (!$projectDB->deleteDocument($token)) { - throw new Exception('Failed to remove token from DB', 500); + /** + * We act like we're updating and validating + * the recovery token but actually we don't need it anymore. + */ + if (!$projectDB->deleteDocument($recovery)) { + throw new Exception('Failed to remove recovery from DB', 500); } $audit @@ -1048,6 +1096,8 @@ $utopia->put('/v1/account/recovery') ->setParam('event', 'account.recovery.update') ; - $response->json(array('result' => 'success')); + $recovery = $profile->search('$uid', $recovery, $profile->getAttribute('tokens', [])); + + $response->json($recovery->getArrayCopy(['$uid', 'type', 'expire'])); } ); \ No newline at end of file diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 2fdcad5ee..55b590a4e 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -378,7 +378,7 @@ $utopia->post('/v1/teams/:teamId/memberships') try { $mail->send(); } catch (\Exception $error) { - //throw new Exception('Problem sending mail: ' . $error->getMessage(), 500); + throw new Exception('Error sending mail: ' . $error->getMessage(), 500); } $audit @@ -457,7 +457,7 @@ $utopia->post('/v1/teams/:teamId/memberships/:inviteId/resend') try { $mail->send(); } catch (\Exception $error) { - //throw new Exception('Problem sending mail: ' . $error->getMessage(), 500); + throw new Exception('Error sending mail: ' . $error->getMessage(), 500); } $audit diff --git a/app/init.php b/app/init.php index 8ee73aae2..46d659ea4 100644 --- a/app/init.php +++ b/app/init.php @@ -111,8 +111,8 @@ $register->set('smtp', function () use ($request) { $mail->isSMTP(); - $username = $request->getServer('_APP_SMTP_USERNAME', ''); - $password = $request->getServer('_APP_SMTP_PASSWORD', ''); + $username = $request->getServer('_APP_SMTP_USERNAME', null); + $password = $request->getServer('_APP_SMTP_PASSWORD', null); $mail->XMailer = 'Appwrite Mailer'; $mail->Host = $request->getServer('_APP_SMTP_HOST', 'smtp'); @@ -120,7 +120,8 @@ $register->set('smtp', function () use ($request) { $mail->SMTPAuth = (!empty($username) && !empty($password)); $mail->Username = $username; $mail->Password = $password; - $mail->SMTPSecure = $request->getServer('_APP_SMTP_SECURE', ''); + $mail->SMTPSecure = $request->getServer('_APP_SMTP_SECURE', false); + $mail->SMTPAutoTLS = false; $mail->setFrom('team@appwrite.io', APP_NAME.' Team'); $mail->addReplyTo('team@appwrite.io', APP_NAME.' Team'); diff --git a/docker-compose.yml b/docker-compose.yml index cb49a945c..28620ee05 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,6 +17,7 @@ services: - ./phpunit.xml:/usr/share/nginx/html/phpunit.xml - ./tests:/usr/share/nginx/html/tests - ./app:/usr/share/nginx/html/app + - ./vendor:/usr/share/nginx/html/vendor - ./docs:/usr/share/nginx/html/docs - ./public:/usr/share/nginx/html/public - ./src:/usr/share/nginx/html/src @@ -31,6 +32,7 @@ services: - clamav - influxdb - telegraf + - maildev environment: - _APP_ENV=development - _APP_OPTIONS_ABUSE=disabled @@ -46,6 +48,8 @@ services: - _APP_INFLUXDB_PORT=8086 - _APP_STATSD_HOST=telegraf - _APP_STATSD_PORT=8125 + - _APP_SMTP_HOST=maildev + - _APP_SMTP_PORT=25 mariadb: image: appwrite/mariadb:1.0.2 # fix issues when upgrading using: mysql_upgrade -u root -p @@ -100,18 +104,25 @@ services: networks: - appwrite - # resque: - # image: registry.gitlab.com/appwrite/appwrite/resque-web:v1.0.2 - # restart: unless-stopped - # networks: - # - appwrite - # ports: - # - "5678:5678" - # environment: - # - RESQUE_WEB_HOST=redis - # - RESQUE_WEB_PORT=6379 - # - RESQUE_WEB_HTTP_BASIC_AUTH_USER=user - # - RESQUE_WEB_HTTP_BASIC_AUTH_PASSWORD=password + resque: + image: registry.gitlab.com/appwrite/appwrite/resque-web:v1.0.2 + restart: unless-stopped + networks: + - appwrite + ports: + - "5678:5678" + environment: + - RESQUE_WEB_HOST=redis + - RESQUE_WEB_PORT=6379 + - RESQUE_WEB_HTTP_BASIC_AUTH_USER=user + - RESQUE_WEB_HTTP_BASIC_AUTH_PASSWORD=password + + maildev: + ports: + - '1080:80' + networks: + - appwrite + image: djfarrelly/maildev networks: appwrite: diff --git a/tests/e2e/AccountBase.php b/tests/e2e/AccountBase.php new file mode 100644 index 000000000..bae6bea2d --- /dev/null +++ b/tests/e2e/AccountBase.php @@ -0,0 +1,920 @@ +client->call(Client::METHOD_POST, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => $password, + 'name' => $name, + ]); + + $uid = $response['body']['$uid']; + + $this->assertEquals($response['headers']['status-code'], 201); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$uid']); + $this->assertIsNumeric($response['body']['registration']); + $this->assertEquals($response['body']['email'], $email); + $this->assertEquals($response['body']['name'], $name); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_POST, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => $password, + 'name' => $name, + ]); + + $this->assertEquals($response['headers']['status-code'], 409); + + return [ + 'uid' => $uid, + 'email' => $email, + 'password' => $password, + 'name' => $name, + ]; + } + + /** + * @depends testCreateAccount + */ + public function testCreateAccountSession($data):array + { + $email = (isset($data['email'])) ? $data['email'] : ''; + $password = (isset($data['password'])) ? $data['password'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => $password, + ]); + + $sessionUid = $response['body']['$uid']; + $session = $this->client->parseCookie($response['headers']['set-cookie'])['a_session_console']; + + $this->assertEquals($response['headers']['status-code'], 201); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email.'x', + 'password' => $password, + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => $password.'x', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => '', + 'password' => '', + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + return array_merge($data, [ + 'sessionUid' => $sessionUid, + 'session' => $session, + ]); + } + + /** + * @depends testCreateAccountSession + */ + public function testGetAccount($data):array + { + $email = (isset($data['email'])) ? $data['email'] : ''; + $name = (isset($data['name'])) ? $data['name'] : ''; + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$uid']); + $this->assertIsNumeric($response['body']['registration']); + $this->assertEquals($response['body']['email'], $email); + $this->assertEquals($response['body']['name'], $name); + $this->assertContains('*', $response['body']['roles']); + $this->assertContains('user:'.$response['body']['$uid'], $response['body']['roles']); + $this->assertContains('role:1', $response['body']['roles']); + $this->assertCount(3, $response['body']['roles']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session.'xx', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return $data; + } + + /** + * @depends testCreateAccountSession + */ + public function testGetAccountPrefs($data):array + { + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/account/prefs', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertEmpty($response['body']); + $this->assertCount(0, $response['body']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/account/prefs', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return $data; + } + + /** + * @depends testCreateAccountSession + */ + public function testGetAccountSessions($data):array + { + $session = (isset($data['session'])) ? $data['session'] : ''; + $sessionUid = (isset($data['sessionUid'])) ? $data['sessionUid'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertCount(1, $response['body']); + $this->assertEquals($sessionUid, $response['body'][0]['$uid']); + + $this->assertIsArray($response['body'][0]['OS']); + $this->assertEquals('Windows', $response['body'][0]['OS']['name']); + $this->assertEquals('WIN', $response['body'][0]['OS']['short_name']); + $this->assertEquals('10', $response['body'][0]['OS']['version']); + $this->assertEquals('x64', $response['body'][0]['OS']['platform']); + + $this->assertIsArray($response['body'][0]['client']); + $this->assertEquals('browser', $response['body'][0]['client']['type']); + $this->assertEquals('Chrome', $response['body'][0]['client']['name']); + $this->assertEquals('CH', $response['body'][0]['client']['short_name']); // FIXME (v1) key name should be camelcase + $this->assertEquals('70.0', $response['body'][0]['client']['version']); + $this->assertEquals('Blink', $response['body'][0]['client']['engine']); + $this->assertEquals(0, $response['body'][0]['device']); + $this->assertEquals('', $response['body'][0]['brand']); + $this->assertEquals('', $response['body'][0]['model']); + $this->assertEquals($response['body'][0]['ip'], filter_var($response['body'][0]['ip'], FILTER_VALIDATE_IP)); + + $this->assertIsArray($response['body'][0]['geo']); + $this->assertEquals('--', $response['body'][0]['geo']['isoCode']); + $this->assertEquals('Unknown', $response['body'][0]['geo']['country']); + + $this->assertEquals(true, $response['body'][0]['current']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return $data; + } + + /** + * @depends testCreateAccountSession + */ + public function testGetAccountLogs($data):array + { + sleep(5); + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/account/logs', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertCount(2, $response['body']); + + $this->assertEquals('account.create', $response['body'][0]['event']); + $this->assertEquals($response['body'][0]['ip'], filter_var($response['body'][0]['ip'], FILTER_VALIDATE_IP)); + $this->assertIsNumeric($response['body'][0]['time']); + + $this->assertIsArray($response['body'][0]['OS']); + $this->assertEquals('Windows', $response['body'][0]['OS']['name']); + $this->assertEquals('WIN', $response['body'][0]['OS']['short_name']); + $this->assertEquals('10', $response['body'][0]['OS']['version']); + $this->assertEquals('x64', $response['body'][0]['OS']['platform']); + + $this->assertIsArray($response['body'][0]['client']); + $this->assertEquals('browser', $response['body'][0]['client']['type']); + $this->assertEquals('Chrome', $response['body'][0]['client']['name']); + $this->assertEquals('CH', $response['body'][0]['client']['short_name']); // FIXME (v1) key name should be camelcase + $this->assertEquals('70.0', $response['body'][0]['client']['version']); + $this->assertEquals('Blink', $response['body'][0]['client']['engine']); + $this->assertEquals(0, $response['body'][0]['device']); + $this->assertEquals('', $response['body'][0]['brand']); + $this->assertEquals('', $response['body'][0]['model']); + $this->assertEquals($response['body'][0]['ip'], filter_var($response['body'][0]['ip'], FILTER_VALIDATE_IP)); + + $this->assertIsArray($response['body'][0]['geo']); + $this->assertEquals('--', $response['body'][0]['geo']['isoCode']); + $this->assertEquals('Unknown', $response['body'][0]['geo']['country']); + + $this->assertEquals('account.sessions.create', $response['body'][1]['event']); + $this->assertEquals($response['body'][1]['ip'], filter_var($response['body'][0]['ip'], FILTER_VALIDATE_IP)); + $this->assertIsNumeric($response['body'][1]['time']); + + $this->assertIsArray($response['body'][1]['OS']); + $this->assertEquals('Windows', $response['body'][1]['OS']['name']); + $this->assertEquals('WIN', $response['body'][1]['OS']['short_name']); + $this->assertEquals('10', $response['body'][1]['OS']['version']); + $this->assertEquals('x64', $response['body'][1]['OS']['platform']); + + $this->assertIsArray($response['body'][1]['client']); + $this->assertEquals('browser', $response['body'][1]['client']['type']); + $this->assertEquals('Chrome', $response['body'][1]['client']['name']); + $this->assertEquals('CH', $response['body'][1]['client']['short_name']); // FIXME (v1) key name should be camelcase + $this->assertEquals('70.0', $response['body'][1]['client']['version']); + $this->assertEquals('Blink', $response['body'][1]['client']['engine']); + $this->assertEquals(0, $response['body'][1]['device']); + $this->assertEquals('', $response['body'][1]['brand']); + $this->assertEquals('', $response['body'][1]['model']); + $this->assertEquals($response['body'][1]['ip'], filter_var($response['body'][0]['ip'], FILTER_VALIDATE_IP)); + + $this->assertIsArray($response['body'][1]['geo']); + $this->assertEquals('--', $response['body'][1]['geo']['isoCode']); + $this->assertEquals('Unknown', $response['body'][1]['geo']['country']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/account/logs', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return $data; + } + + //TODO Add tests for OAuth session creation + + /** + * @depends testCreateAccountSession + */ + public function testUpdateAccountName($data):array + { + $email = (isset($data['email'])) ? $data['email'] : ''; + $session = (isset($data['session'])) ? $data['session'] : ''; + $newName = 'New Name'; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/name', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + 'name' => $newName + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$uid']); + $this->assertIsNumeric($response['body']['registration']); + $this->assertEquals($response['body']['email'], $email); + $this->assertEquals($response['body']['name'], $newName); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/name', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + $response = $this->client->call(Client::METHOD_PATCH, '/account/name', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + $response = $this->client->call(Client::METHOD_PATCH, '/account/name', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + 'name' => 'ocSRq1d3QphHivJyUmYY7WMnrxyjdk5YvVwcDqx2zS0coxESN8RmsQwLWw5Whnf0WbVohuFWTRAaoKgCOO0Y0M7LwgFnZmi8881Y7' + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + $data['name'] = $newName; + + return $data; + } + + /** + * @depends testUpdateAccountName + */ + public function testUpdateAccountPassword($data):array + { + $email = (isset($data['email'])) ? $data['email'] : ''; + $password = (isset($data['password'])) ? $data['password'] : ''; + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/password', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + 'password' => 'new-password', + 'old-password' => $password, + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$uid']); + $this->assertIsNumeric($response['body']['registration']); + $this->assertEquals($response['body']['email'], $email); + $this->assertEquals($response['body']['name'], 'New Name'); + + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => 'new-password', + ]); + + $this->assertEquals($response['headers']['status-code'], 201); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/password', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + $response = $this->client->call(Client::METHOD_PATCH, '/account/password', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + $data['password'] = 'new-password'; + + return $data; + } + + /** + * @depends testUpdateAccountPassword + */ + public function testUpdateAccountEmail($data):array + { + $newEmail = uniqid().'new@localhost.test'; + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/email', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + 'email' => $newEmail, + 'password' => 'new-password', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$uid']); + $this->assertIsNumeric($response['body']['registration']); + $this->assertEquals($response['body']['email'], $newEmail); + $this->assertEquals($response['body']['name'], 'New Name'); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/email', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + $response = $this->client->call(Client::METHOD_PATCH, '/account/email', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + $data['email'] = $newEmail; + + return $data; + } + + /** + * @depends testUpdateAccountEmail + */ + public function testUpdateAccountPrefs($data):array + { + $newEmail = uniqid().'new@localhost.test'; + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/prefs', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + 'prefs' => [ + 'key1' => 'value1', + 'key2' => 'value2', + ] + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertEquals('value1', $response['body']['key1']); + $this->assertEquals('value2', $response['body']['key2']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/prefs', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return $data; + } + + /** + * @depends testUpdateAccountPrefs + */ + public function testDeleteAccountSession($data):array + { + $email = (isset($data['email'])) ? $data['email'] : ''; + $password = (isset($data['password'])) ? $data['password'] : ''; + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => $password, + ]); + + $sessionNewUid = $response['body']['$uid']; + $sessionNew = $this->client->parseCookie($response['headers']['set-cookie'])['a_session_console']; + + $this->assertEquals($response['headers']['status-code'], 201); + + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $sessionNew, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + + $response = $this->client->call(Client::METHOD_DELETE, '/account/sessions/'.$sessionNewUid, [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 204); + + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $sessionNew, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return $data; + } + + /** + * @depends testUpdateAccountPrefs + */ + public function testDeleteAccountSessionCurrent($data):array + { + $email = (isset($data['email'])) ? $data['email'] : ''; + $password = (isset($data['password'])) ? $data['password'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => $password, + ]); + + $sessionNew = $this->client->parseCookie($response['headers']['set-cookie'])['a_session_console']; + + $this->assertEquals($response['headers']['status-code'], 201); + + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $sessionNew, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + + $response = $this->client->call(Client::METHOD_DELETE, '/account/sessions/current', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $sessionNew, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 204); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $sessionNew, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return $data; + } + + /** + * @depends testUpdateAccountPrefs + */ + public function testDeleteAccountSessions($data):array + { + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_DELETE, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 204); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + /** + * Create new fallback session + */ + $email = (isset($data['email'])) ? $data['email'] : ''; + $password = (isset($data['password'])) ? $data['password'] : ''; + + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => $password, + ]); + + $data['session'] = $this->client->parseCookie($response['headers']['set-cookie'])['a_session_console']; + + return $data; + } + + /** + * @depends testDeleteAccountSession + */ + public function testCreateAccountRecovery($data):array + { + $email = (isset($data['email'])) ? $data['email'] : ''; + $name = (isset($data['name'])) ? $data['name'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_POST, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'reset' => 'http://localhost/recovery', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertNotEmpty(3, $response['body']['$uid']); + $this->assertEquals(3, $response['body']['type']); + $this->assertIsNumeric($response['body']['expire']); + + $lastEmail = $this->getLastEmail(); + + $this->assertEquals($email, $lastEmail['to'][0]['address']); + $this->assertEquals($name, $lastEmail['to'][0]['name']); + $this->assertEquals('Password Reset', $lastEmail['subject']); + + $recovery = substr($lastEmail['text'], strpos($lastEmail['text'], '&token=', 0) + 7, 256); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_POST, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'reset' => 'localhost/recovery', + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'reset' => 'http://remotehost/recovery', + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => 'not-found@localhost.test', + 'reset' => 'http://localhost/recovery', + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + $data['recovery'] = $recovery; + + return $data; + } + + /** + * @depends testCreateAccountRecovery + */ + public function testUpdateAccountRecovery($data):array + { + $uid = (isset($data['uid'])) ? $data['uid'] : ''; + $recovery = (isset($data['recovery'])) ? $data['recovery'] : ''; + $newPassowrd = 'test-recovery'; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_PUT, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'userId' => $uid, + 'token' => $recovery, + 'password-a' => $newPassowrd, + 'password-b' => $newPassowrd, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_PUT, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'userId' => 'ewewe', + 'token' => $recovery, + 'password-a' => $newPassowrd, + 'password-b' => $newPassowrd, + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_PUT, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'userId' => $uid, + 'token' => 'sdasdasdasd', + 'password-a' => $newPassowrd, + 'password-b' => $newPassowrd, + ]); + + $this->assertEquals(401, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_PUT, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'userId' => $uid, + 'token' => $recovery, + 'password-a' => $newPassowrd.'x', + 'password-b' => $newPassowrd, + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + return $data; + } +} \ No newline at end of file diff --git a/tests/e2e/AccountConsoleClientTest.php b/tests/e2e/AccountConsoleClientTest.php new file mode 100644 index 000000000..e6b9dd6b6 --- /dev/null +++ b/tests/e2e/AccountConsoleClientTest.php @@ -0,0 +1,14 @@ + 'console', + 'name' => 'Appwrite', + 'apiKey' => '', + ]; + } +} diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php new file mode 100644 index 000000000..873e1353b --- /dev/null +++ b/tests/e2e/Scopes/ProjectCustom.php @@ -0,0 +1,119 @@ +project)) { + return $this->project; + } + + $root = $this->client->call(Client::METHOD_POST, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $this->rootEmail, + 'password' => $this->rootEmail, + 'name' => 'Demo User', + ]); + + $this->assertEquals(201, $root['headers']['status-code']); + + $session = $this->client->parseCookie($root['headers']['set-cookie'])['a_session_console']; + + $team = $this->client->call(Client::METHOD_POST, '/teams', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + 'name' => 'Demo Project Team', + ]); + + $this->assertEquals(201, $team['headers']['status-code']); + $this->assertEquals('Demo Project Team', $team['body']['name']); + $this->assertNotEmpty($team['body']['$uid']); + + $project = $this->client->call(Client::METHOD_POST, '/projects', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + 'name' => 'Demo Project', + 'teamId' => $team['body']['$uid'], + 'description' => 'Demo Project Description', + 'logo' => '', + 'url' => 'https://appwrite.io', + 'legalName' => '', + 'legalCountry' => '', + 'legalState' => '', + 'legalCity' => '', + 'legalAddress' => '', + 'legalTaxId' => '', + ]); + + $this->assertEquals(201, $project['headers']['status-code']); + $this->assertNotEmpty($project['body']); + + $key = $this->client->call(Client::METHOD_POST, '/projects/' . $project['body']['$uid'] . '/keys', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + 'name' => 'Demo Project Key', + 'scopes' => [ + 'account', + '', + '', + ], + ]); + + $this->assertEquals(201, $project['headers']['status-code']); + $this->assertNotEmpty($key['body']); + $this->assertNotEmpty($key['body']['secret']); + + // return [ + // 'email' => $this->demoEmail, + // 'password' => $this->demoPassword, + // 'session' => $session, + // 'projectUid' => $project['body']['$uid'], + // 'projectAPIKeySecret' => $key['body']['secret'], + // 'projectSession' => $this->client->parseCookie($user['headers']['set-cookie'])['a_session_' . $project['body']['$uid']], + // ]; + + $this->project = [ + '$uid' => $project['body']['$uid'], + 'name' => $project['body']['name'], + 'apiKey' => $key['body']['secret'], + ]; + + var_dump('init project'); + + return $this->project; + } +} diff --git a/tests/e2e/Scopes/Scope.php b/tests/e2e/Scopes/Scope.php new file mode 100644 index 000000000..60937da0b --- /dev/null +++ b/tests/e2e/Scopes/Scope.php @@ -0,0 +1,55 @@ +client = new Client(); + + $this->client + ->setEndpoint($this->endpoint) + ; + } + + protected function tearDown(): void + { + $this->client = null; + } + + protected function getLastEmail():array + { + sleep(3); + $emails = json_decode(file_get_contents('http://maildev/email'), true); + + if($emails && is_array($emails)) { + return end($emails); + } + + return []; + } + + /** + * @return array + */ + abstract public function getHeaders():array; + + /** + * @return array + */ + abstract public function getProject():array; +} diff --git a/tests/e2e/Scopes/SideClient.php b/tests/e2e/Scopes/SideClient.php new file mode 100644 index 000000000..2e442ef7a --- /dev/null +++ b/tests/e2e/Scopes/SideClient.php @@ -0,0 +1,11 @@ + $this->getProject() + ]; + } +} diff --git a/tests/old/AccountTest.php b/tests/old/AccountTest.php new file mode 100644 index 000000000..c60026127 --- /dev/null +++ b/tests/old/AccountTest.php @@ -0,0 +1,930 @@ +demo(); } +} + +class AccountTest extends Base +{ + use TraitDemo; + + public function demo() { + var_dump(4321); + } + + public function testCreateAccount():array + { + $email = uniqid().'user@localhost.test'; + $password = 'passwrod'; + $name = 'User Name'; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_POST, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => $password, + 'name' => $name, + ]); + + $uid = $response['body']['$uid']; + + $this->assertEquals($response['headers']['status-code'], 201); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$uid']); + $this->assertIsNumeric($response['body']['registration']); + $this->assertEquals($response['body']['email'], $email); + $this->assertEquals($response['body']['name'], $name); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_POST, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => $password, + 'name' => $name, + ]); + + $this->assertEquals($response['headers']['status-code'], 409); + + return [ + 'uid' => $uid, + 'email' => $email, + 'password' => $password, + 'name' => $name, + ]; + } + + /** + * @depends testCreateAccount + */ + public function testCreateAccountSession($data):array + { + $email = (isset($data['email'])) ? $data['email'] : ''; + $password = (isset($data['password'])) ? $data['password'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => $password, + ]); + + $sessionUid = $response['body']['$uid']; + $session = $this->client->parseCookie($response['headers']['set-cookie'])['a_session_console']; + + $this->assertEquals($response['headers']['status-code'], 201); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email.'x', + 'password' => $password, + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => $password.'x', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => '', + 'password' => '', + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + return array_merge($data, [ + 'sessionUid' => $sessionUid, + 'session' => $session, + ]); + } + + /** + * @depends testCreateAccountSession + */ + public function testGetAccount($data):array + { + $email = (isset($data['email'])) ? $data['email'] : ''; + $name = (isset($data['name'])) ? $data['name'] : ''; + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$uid']); + $this->assertIsNumeric($response['body']['registration']); + $this->assertEquals($response['body']['email'], $email); + $this->assertEquals($response['body']['name'], $name); + $this->assertContains('*', $response['body']['roles']); + $this->assertContains('user:'.$response['body']['$uid'], $response['body']['roles']); + $this->assertContains('role:1', $response['body']['roles']); + $this->assertCount(3, $response['body']['roles']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session.'xx', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return $data; + } + + /** + * @depends testCreateAccountSession + */ + public function testGetAccountPrefs($data):array + { + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/account/prefs', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertEmpty($response['body']); + $this->assertCount(0, $response['body']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/account/prefs', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return $data; + } + + /** + * @depends testCreateAccountSession + */ + public function testGetAccountSessions($data):array + { + $session = (isset($data['session'])) ? $data['session'] : ''; + $sessionUid = (isset($data['sessionUid'])) ? $data['sessionUid'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertCount(1, $response['body']); + $this->assertEquals($sessionUid, $response['body'][0]['$uid']); + + $this->assertIsArray($response['body'][0]['OS']); + $this->assertEquals('Windows', $response['body'][0]['OS']['name']); + $this->assertEquals('WIN', $response['body'][0]['OS']['short_name']); + $this->assertEquals('10', $response['body'][0]['OS']['version']); + $this->assertEquals('x64', $response['body'][0]['OS']['platform']); + + $this->assertIsArray($response['body'][0]['client']); + $this->assertEquals('browser', $response['body'][0]['client']['type']); + $this->assertEquals('Chrome', $response['body'][0]['client']['name']); + $this->assertEquals('CH', $response['body'][0]['client']['short_name']); // FIXME (v1) key name should be camelcase + $this->assertEquals('70.0', $response['body'][0]['client']['version']); + $this->assertEquals('Blink', $response['body'][0]['client']['engine']); + $this->assertEquals(0, $response['body'][0]['device']); + $this->assertEquals('', $response['body'][0]['brand']); + $this->assertEquals('', $response['body'][0]['model']); + $this->assertEquals($response['body'][0]['ip'], filter_var($response['body'][0]['ip'], FILTER_VALIDATE_IP)); + + $this->assertIsArray($response['body'][0]['geo']); + $this->assertEquals('--', $response['body'][0]['geo']['isoCode']); + $this->assertEquals('Unknown', $response['body'][0]['geo']['country']); + + $this->assertEquals(true, $response['body'][0]['current']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return $data; + } + + /** + * @depends testCreateAccountSession + */ + public function testGetAccountLogs($data):array + { + sleep(5); + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/account/logs', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertCount(2, $response['body']); + + $this->assertEquals('account.create', $response['body'][0]['event']); + $this->assertEquals($response['body'][0]['ip'], filter_var($response['body'][0]['ip'], FILTER_VALIDATE_IP)); + $this->assertIsNumeric($response['body'][0]['time']); + + $this->assertIsArray($response['body'][0]['OS']); + $this->assertEquals('Windows', $response['body'][0]['OS']['name']); + $this->assertEquals('WIN', $response['body'][0]['OS']['short_name']); + $this->assertEquals('10', $response['body'][0]['OS']['version']); + $this->assertEquals('x64', $response['body'][0]['OS']['platform']); + + $this->assertIsArray($response['body'][0]['client']); + $this->assertEquals('browser', $response['body'][0]['client']['type']); + $this->assertEquals('Chrome', $response['body'][0]['client']['name']); + $this->assertEquals('CH', $response['body'][0]['client']['short_name']); // FIXME (v1) key name should be camelcase + $this->assertEquals('70.0', $response['body'][0]['client']['version']); + $this->assertEquals('Blink', $response['body'][0]['client']['engine']); + $this->assertEquals(0, $response['body'][0]['device']); + $this->assertEquals('', $response['body'][0]['brand']); + $this->assertEquals('', $response['body'][0]['model']); + $this->assertEquals($response['body'][0]['ip'], filter_var($response['body'][0]['ip'], FILTER_VALIDATE_IP)); + + $this->assertIsArray($response['body'][0]['geo']); + $this->assertEquals('--', $response['body'][0]['geo']['isoCode']); + $this->assertEquals('Unknown', $response['body'][0]['geo']['country']); + + $this->assertEquals('account.sessions.create', $response['body'][1]['event']); + $this->assertEquals($response['body'][1]['ip'], filter_var($response['body'][0]['ip'], FILTER_VALIDATE_IP)); + $this->assertIsNumeric($response['body'][1]['time']); + + $this->assertIsArray($response['body'][1]['OS']); + $this->assertEquals('Windows', $response['body'][1]['OS']['name']); + $this->assertEquals('WIN', $response['body'][1]['OS']['short_name']); + $this->assertEquals('10', $response['body'][1]['OS']['version']); + $this->assertEquals('x64', $response['body'][1]['OS']['platform']); + + $this->assertIsArray($response['body'][1]['client']); + $this->assertEquals('browser', $response['body'][1]['client']['type']); + $this->assertEquals('Chrome', $response['body'][1]['client']['name']); + $this->assertEquals('CH', $response['body'][1]['client']['short_name']); // FIXME (v1) key name should be camelcase + $this->assertEquals('70.0', $response['body'][1]['client']['version']); + $this->assertEquals('Blink', $response['body'][1]['client']['engine']); + $this->assertEquals(0, $response['body'][1]['device']); + $this->assertEquals('', $response['body'][1]['brand']); + $this->assertEquals('', $response['body'][1]['model']); + $this->assertEquals($response['body'][1]['ip'], filter_var($response['body'][0]['ip'], FILTER_VALIDATE_IP)); + + $this->assertIsArray($response['body'][1]['geo']); + $this->assertEquals('--', $response['body'][1]['geo']['isoCode']); + $this->assertEquals('Unknown', $response['body'][1]['geo']['country']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/account/logs', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return $data; + } + + //TODO Add tests for OAuth session creation + + /** + * @depends testCreateAccountSession + */ + public function testUpdateAccountName($data):array + { + $email = (isset($data['email'])) ? $data['email'] : ''; + $session = (isset($data['session'])) ? $data['session'] : ''; + $newName = 'New Name'; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/name', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + 'name' => $newName + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$uid']); + $this->assertIsNumeric($response['body']['registration']); + $this->assertEquals($response['body']['email'], $email); + $this->assertEquals($response['body']['name'], $newName); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/name', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + $response = $this->client->call(Client::METHOD_PATCH, '/account/name', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + $response = $this->client->call(Client::METHOD_PATCH, '/account/name', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + 'name' => 'ocSRq1d3QphHivJyUmYY7WMnrxyjdk5YvVwcDqx2zS0coxESN8RmsQwLWw5Whnf0WbVohuFWTRAaoKgCOO0Y0M7LwgFnZmi8881Y7' + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + $data['name'] = $newName; + + return $data; + } + + /** + * @depends testUpdateAccountName + */ + public function testUpdateAccountPassword($data):array + { + $email = (isset($data['email'])) ? $data['email'] : ''; + $password = (isset($data['password'])) ? $data['password'] : ''; + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/password', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + 'password' => 'new-password', + 'old-password' => $password, + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$uid']); + $this->assertIsNumeric($response['body']['registration']); + $this->assertEquals($response['body']['email'], $email); + $this->assertEquals($response['body']['name'], 'New Name'); + + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => 'new-password', + ]); + + $this->assertEquals($response['headers']['status-code'], 201); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/password', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + $response = $this->client->call(Client::METHOD_PATCH, '/account/password', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + $data['password'] = 'new-password'; + + return $data; + } + + /** + * @depends testUpdateAccountPassword + */ + public function testUpdateAccountEmail($data):array + { + $newEmail = uniqid().'new@localhost.test'; + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/email', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + 'email' => $newEmail, + 'password' => 'new-password', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$uid']); + $this->assertIsNumeric($response['body']['registration']); + $this->assertEquals($response['body']['email'], $newEmail); + $this->assertEquals($response['body']['name'], 'New Name'); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/email', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + $response = $this->client->call(Client::METHOD_PATCH, '/account/email', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + $data['email'] = $newEmail; + + return $data; + } + + /** + * @depends testUpdateAccountEmail + */ + public function testUpdateAccountPrefs($data):array + { + $newEmail = uniqid().'new@localhost.test'; + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/prefs', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ], [ + 'prefs' => [ + 'key1' => 'value1', + 'key2' => 'value2', + ] + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertEquals('value1', $response['body']['key1']); + $this->assertEquals('value2', $response['body']['key2']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/prefs', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return $data; + } + + /** + * @depends testUpdateAccountPrefs + */ + public function testDeleteAccountSession($data):array + { + $email = (isset($data['email'])) ? $data['email'] : ''; + $password = (isset($data['password'])) ? $data['password'] : ''; + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => $password, + ]); + + $sessionNewUid = $response['body']['$uid']; + $sessionNew = $this->client->parseCookie($response['headers']['set-cookie'])['a_session_console']; + + $this->assertEquals($response['headers']['status-code'], 201); + + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $sessionNew, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + + $response = $this->client->call(Client::METHOD_DELETE, '/account/sessions/'.$sessionNewUid, [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 204); + + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $sessionNew, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return $data; + } + + /** + * @depends testUpdateAccountPrefs + */ + public function testDeleteAccountSessionCurrent($data):array + { + $email = (isset($data['email'])) ? $data['email'] : ''; + $password = (isset($data['password'])) ? $data['password'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => $password, + ]); + + $sessionNew = $this->client->parseCookie($response['headers']['set-cookie'])['a_session_console']; + + $this->assertEquals($response['headers']['status-code'], 201); + + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $sessionNew, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + + $response = $this->client->call(Client::METHOD_DELETE, '/account/sessions/current', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $sessionNew, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 204); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $sessionNew, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return $data; + } + + /** + * @depends testUpdateAccountPrefs + */ + public function testDeleteAccountSessions($data):array + { + $session = (isset($data['session'])) ? $data['session'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_DELETE, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 204); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'cookie' => 'a_session_console=' . $session, + 'x-appwrite-project' => 'console', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + /** + * Create new fallback session + */ + $email = (isset($data['email'])) ? $data['email'] : ''; + $password = (isset($data['password'])) ? $data['password'] : ''; + + $response = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'password' => $password, + ]); + + $data['session'] = $this->client->parseCookie($response['headers']['set-cookie'])['a_session_console']; + + return $data; + } + + /** + * @depends testDeleteAccountSession + */ + public function testCreateAccountRecovery($data):array + { + $email = (isset($data['email'])) ? $data['email'] : ''; + $name = (isset($data['name'])) ? $data['name'] : ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_POST, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'reset' => 'http://localhost/recovery', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertNotEmpty(3, $response['body']['$uid']); + $this->assertEquals(3, $response['body']['type']); + $this->assertIsNumeric($response['body']['expire']); + + $lastEmail = $this->getLastEmail(); + + $this->assertEquals($email, $lastEmail['to'][0]['address']); + $this->assertEquals($name, $lastEmail['to'][0]['name']); + $this->assertEquals('Password Reset', $lastEmail['subject']); + + $recovery = substr($lastEmail['text'], strpos($lastEmail['text'], '&token=', 0) + 7, 256); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_POST, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'reset' => 'localhost/recovery', + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => $email, + 'reset' => 'http://remotehost/recovery', + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'email' => 'not-found@localhost.test', + 'reset' => 'http://localhost/recovery', + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + $data['recovery'] = $recovery; + + return $data; + } + + /** + * @depends testCreateAccountRecovery + */ + public function testUpdateAccountRecovery($data):array + { + $uid = (isset($data['uid'])) ? $data['uid'] : ''; + $recovery = (isset($data['recovery'])) ? $data['recovery'] : ''; + $newPassowrd = 'test-recovery'; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_PUT, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'userId' => $uid, + 'token' => $recovery, + 'password-a' => $newPassowrd, + 'password-b' => $newPassowrd, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_PUT, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'userId' => 'ewewe', + 'token' => $recovery, + 'password-a' => $newPassowrd, + 'password-b' => $newPassowrd, + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_PUT, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'userId' => $uid, + 'token' => 'sdasdasdasd', + 'password-a' => $newPassowrd, + 'password-b' => $newPassowrd, + ]); + + $this->assertEquals(401, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_PUT, '/account/recovery', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], [ + 'userId' => $uid, + 'token' => $recovery, + 'password-a' => $newPassowrd.'x', + 'password-b' => $newPassowrd, + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + return $data; + } +} \ No newline at end of file diff --git a/tests/old/Base.php b/tests/old/Base.php new file mode 100644 index 000000000..24104d782 --- /dev/null +++ b/tests/old/Base.php @@ -0,0 +1,41 @@ +client = new Client(); + + $this->client + ->setEndpoint($this->endpoint) + ; + } + + protected function tearDown(): void + { + $this->client = null; + } + + protected function getLastEmail():array + { + sleep(3); + $emails = json_decode(file_get_contents('http://localhost:1080/email'), true); + + if($emails && is_array($emails)) { + return end($emails); + } + + return []; + } +} diff --git a/tests/e2e/BaseConsole.php b/tests/old/BaseConsole.php similarity index 100% rename from tests/e2e/BaseConsole.php rename to tests/old/BaseConsole.php diff --git a/tests/e2e/BaseProjects.php b/tests/old/BaseProjects.php similarity index 100% rename from tests/e2e/BaseProjects.php rename to tests/old/BaseProjects.php diff --git a/tests/e2e/ConsoleHealthTest.php b/tests/old/ConsoleHealthTest.php similarity index 100% rename from tests/e2e/ConsoleHealthTest.php rename to tests/old/ConsoleHealthTest.php diff --git a/tests/e2e/ConsoleProjectsTest.php b/tests/old/ConsoleProjectsTest.php similarity index 100% rename from tests/e2e/ConsoleProjectsTest.php rename to tests/old/ConsoleProjectsTest.php diff --git a/tests/e2e/ConsoleTest.php b/tests/old/ConsoleTest.php similarity index 100% rename from tests/e2e/ConsoleTest.php rename to tests/old/ConsoleTest.php diff --git a/tests/e2e/ProjectAvatarsTest.php b/tests/old/ProjectAvatarsTest.php similarity index 100% rename from tests/e2e/ProjectAvatarsTest.php rename to tests/old/ProjectAvatarsTest.php diff --git a/tests/e2e/ProjectDatabaseTest.php b/tests/old/ProjectDatabaseTest.php similarity index 100% rename from tests/e2e/ProjectDatabaseTest.php rename to tests/old/ProjectDatabaseTest.php diff --git a/tests/e2e/ProjectLocaleTest.php b/tests/old/ProjectLocaleTest.php similarity index 100% rename from tests/e2e/ProjectLocaleTest.php rename to tests/old/ProjectLocaleTest.php diff --git a/tests/e2e/ProjectStorageTest.php b/tests/old/ProjectStorageTest.php similarity index 100% rename from tests/e2e/ProjectStorageTest.php rename to tests/old/ProjectStorageTest.php diff --git a/tests/e2e/ProjectUsersTest.php b/tests/old/ProjectUsersTest.php similarity index 100% rename from tests/e2e/ProjectUsersTest.php rename to tests/old/ProjectUsersTest.php