Rework tests structure. Upgrade codeception to 2.5.3. Merge params configuration into app configuration.

This commit is contained in:
ErickSkrauch
2019-02-20 22:58:52 +03:00
parent 2eacc581be
commit b05dc6816e
248 changed files with 1503 additions and 1339 deletions

View File

@@ -0,0 +1,61 @@
<?php
namespace api\tests\functional;
use api\tests\_pages\SignupRoute;
use api\tests\FunctionalTester;
class EmailConfirmationCest {
public function testConfirmEmailByCorrectKey(FunctionalTester $I) {
$route = new SignupRoute($I);
$I->wantTo('confirm my email using correct activation key');
$route->confirm('HABGCABHJ1234HBHVD');
$I->canSeeResponseContainsJson([
'success' => true,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
$I->canSeeAuthCredentials(true);
}
public function testConfirmEmailByInvalidKey(FunctionalTester $I) {
$route = new SignupRoute($I);
$I->wantTo('see error.key_is_required expected if key is not set');
$route->confirm();
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'key' => 'error.key_required',
],
]);
$I->wantTo('see error.key_not_exists expected if key not exists in database');
$route->confirm('not-exists-key');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'key' => 'error.key_not_exists',
],
]);
}
public function testConfirmByInvalidEmojiString(FunctionalTester $I) {
$route = new SignupRoute($I);
$I->wantTo('try to submit some long emoji string (Sentry ACCOUNTS-43Y)');
$route->confirm(
'ALWAYS 🕔 make sure 👍 to shave 🔪🍑 because ✌️ the last time 🕒 we let 👐😪 a bush 🌳 ' .
'in our lives 👈😜👉 it did 9/11 💥🏢🏢✈️🔥🔥🔥 ALWAYS 🕔 make sure 👍 to shave 🔪🍑 ' .
'because ✌️ the last time 🕒 we let 👐😪 a bush 🌳 in our lives 👈😜👉 it did 9/11 ' .
'💥🏢🏢✈️🔥🔥🔥/'
);
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'key' => 'error.key_not_exists',
],
]);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace api\tests\functional;
use api\tests\FunctionalTester;
class FeedbackCest {
public function testFeedbackWithoutAuth(FunctionalTester $I) {
$I->sendPOST('/api/feedback', [
'subject' => 'Test',
'email' => 'email@ely.by',
'type' => 0,
'message' => 'Hello world',
]);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
public function testFeedbackWithAuth(FunctionalTester $I) {
$I->amAuthenticated();
$I->sendPOST('/api/feedback', [
'subject' => 'Test',
'email' => 'email@ely.by',
'type' => 0,
'message' => 'Hello world',
]);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace api\tests\functional;
use api\tests\_pages\AuthenticationRoute;
use api\tests\FunctionalTester;
class ForgotPasswordCest {
/**
* @var AuthenticationRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AuthenticationRoute($I);
}
public function testWrongInput(FunctionalTester $I) {
$I->wantTo('see reaction on invalid input');
$this->route->forgotPassword();
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'login' => 'error.login_required',
],
]);
$this->route->forgotPassword('becauseimbatman!');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'login' => 'error.login_not_exist',
],
]);
}
public function testForgotPasswordByEmail(FunctionalTester $I) {
$I->wantTo('create new password recover request by passing email');
$this->route->forgotPassword('admin@ely.by');
$this->assertSuccessResponse($I, false);
}
public function testForgotPasswordByUsername(FunctionalTester $I) {
$I->wantTo('create new password recover request by passing username');
$this->route->forgotPassword('Admin');
$this->assertSuccessResponse($I, true);
}
public function testDataForFrequencyError(FunctionalTester $I) {
$I->wantTo('get info about time to repeat recover password request');
$this->route->forgotPassword('Notch');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'login' => 'error.recently_sent_message',
],
]);
$I->canSeeResponseJsonMatchesJsonPath('$.data.canRepeatIn');
$I->canSeeResponseJsonMatchesJsonPath('$.data.repeatFrequency');
}
/**
* @param FunctionalTester $I
*/
private function assertSuccessResponse(FunctionalTester $I, bool $expectEmailMask = false): void {
$I->canSeeResponseContainsJson([
'success' => true,
]);
$I->canSeeResponseJsonMatchesJsonPath('$.data.canRepeatIn');
$I->canSeeResponseJsonMatchesJsonPath('$.data.repeatFrequency');
if ($expectEmailMask) {
$I->canSeeResponseJsonMatchesJsonPath('$.data.emailMask');
}
}
}

View File

@@ -0,0 +1,218 @@
<?php
namespace api\tests\functional;
use api\tests\FunctionalTester;
use OTPHP\TOTP;
use api\tests\_pages\AuthenticationRoute;
class LoginCest {
public function testLoginEmailOrUsername(FunctionalTester $I) {
$route = new AuthenticationRoute($I);
$I->wantTo('see error.login_required expected if login is not set');
$route->login();
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'login' => 'error.login_required',
],
]);
$I->wantTo('see error.login_not_exist expected if username not exists in database');
$route->login('non-exist-username');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'login' => 'error.login_not_exist',
],
]);
$I->wantTo('see error.login_not_exist expected if email not exists in database');
$route->login('not-exist@user.com');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'login' => 'error.login_not_exist',
],
]);
$I->wantTo('see error.account_not_activated expected if credentials are valid, but account is not activated');
$route->login('howe.garnett', 'password_0');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'login' => 'error.account_not_activated',
],
]);
$I->canSeeResponseJsonMatchesJsonPath('$.data.email');
$I->wantTo('don\'t see errors on login field if username is correct and exists in database');
$route->login('Admin');
$I->canSeeResponseContainsJson([
'success' => false,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.login');
$I->wantTo('don\'t see errors on login field if email is correct and exists in database');
$route->login('admin@ely.by');
$I->canSeeResponseContainsJson([
'success' => false,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.login');
}
public function testLoginPassword(FunctionalTester $I) {
$route = new AuthenticationRoute($I);
$I->wantTo('see password doesn\'t have errors if email or username not set');
$route->login();
$I->canSeeResponseContainsJson([
'success' => false,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.password');
$I->wantTo('see password doesn\'t have errors if username not exists in database');
$route->login('non-exist-username', 'random-password');
$I->canSeeResponseContainsJson([
'success' => false,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.password');
$I->wantTo('see password doesn\'t has errors if email not exists in database');
$route->login('not-exist@user.com', 'random-password');
$I->canSeeResponseContainsJson([
'success' => false,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.password');
$I->wantTo('see error.password_incorrect if email correct, but password wrong');
$route->login('admin@ely.by', 'wrong-password');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'password' => 'error.password_incorrect',
],
]);
$I->wantTo('see error.password_incorrect if username correct, but password wrong');
$route->login('Admin', 'wrong-password');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'password' => 'error.password_incorrect',
],
]);
}
public function testLoginToken(FunctionalTester $I) {
$route = new AuthenticationRoute($I);
$I->wantTo('see totp don\'t have errors if email, username or totp not set');
$route->login();
$I->canSeeResponseContainsJson([
'success' => false,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.totp');
$I->wantTo('see totp don\'t have errors if username not exists in database');
$route->login('non-exist-username', 'random-password');
$I->canSeeResponseContainsJson([
'success' => false,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.totp');
$I->wantTo('see totp don\'t has errors if email not exists in database');
$route->login('not-exist@user.com', 'random-password');
$I->canSeeResponseContainsJson([
'success' => false,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.totp');
$I->wantTo('see totp don\'t has errors if email correct, but password wrong');
$route->login('not-exist@user.com', 'random-password');
$I->canSeeResponseContainsJson([
'success' => false,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.totp');
$I->wantTo('see error.totp_required if username and password correct, but account have enable otp');
$route->login('AccountWithEnabledOtp', 'password_0');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'totp' => 'error.totp_required',
],
]);
$I->wantTo('see error.totp_incorrect if username and password correct, but totp wrong');
$route->login('AccountWithEnabledOtp', 'password_0', '123456');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'totp' => 'error.totp_incorrect',
],
]);
}
public function testLoginByUsernameCorrect(FunctionalTester $I) {
$route = new AuthenticationRoute($I);
$I->wantTo('login into account using correct username and password');
$route->login('Admin', 'password_0');
$I->canSeeResponseContainsJson([
'success' => true,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
$I->canSeeAuthCredentials(false);
}
public function testLoginByEmailCorrect(FunctionalTester $I) {
$route = new AuthenticationRoute($I);
$I->wantTo('login into account using correct email and password');
$route->login('admin@ely.by', 'password_0');
$I->canSeeResponseContainsJson([
'success' => true,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
$I->canSeeAuthCredentials(false);
}
public function testLoginInAccWithPasswordMethod(FunctionalTester $I) {
$route = new AuthenticationRoute($I);
$I->wantTo('login into account with old password hash function using correct username and password');
$route->login('AccWithOldPassword', '12345678');
$I->canSeeResponseContainsJson([
'success' => true,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
$I->canSeeAuthCredentials(false);
}
public function testLoginByEmailWithRemember(FunctionalTester $I) {
$route = new AuthenticationRoute($I);
$I->wantTo('login into account using correct data and get refresh_token');
$route->login('admin@ely.by', 'password_0', true);
$I->canSeeResponseContainsJson([
'success' => true,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
$I->canSeeAuthCredentials(true);
}
public function testLoginByAccountWithOtp(FunctionalTester $I) {
$route = new AuthenticationRoute($I);
$I->wantTo('login into account with enabled otp');
$route->login('AccountWithEnabledOtp', 'password_0', (TOTP::create('BBBB'))->now());
$I->canSeeResponseContainsJson([
'success' => true,
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
$I->canSeeAuthCredentials(false);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace api\tests\functional;
use api\tests\_pages\AuthenticationRoute;
use api\tests\FunctionalTester;
class LogoutCest {
public function testLoginEmailOrUsername(FunctionalTester $I) {
$route = new AuthenticationRoute($I);
$I->amAuthenticated();
$route->logout();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace api\tests\functional;
use api\tests\_pages\OptionsRoute;
use api\tests\FunctionalTester;
class OptionsCest {
/**
* @var OptionsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new OptionsRoute($I);
}
public function testRecaptchaPublicKey(FunctionalTester $I) {
$I->wantTo('Get recaptcha public key');
$this->route->get();
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'reCaptchaPublicKey' => 'public-key',
]);
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace api\tests\functional;
use api\tests\_pages\AccountsRoute;
use api\tests\_pages\AuthenticationRoute;
use api\tests\FunctionalTester;
class RecoverPasswordCest {
public function testDataForFrequencyError(FunctionalTester $I) {
$authRoute = new AuthenticationRoute($I);
$I->wantTo('change my account password, using key from email');
$authRoute->recoverPassword('H24HBDCHHAG2HGHGHS', '12345678', '12345678');
$I->canSeeResponseContainsJson([
'success' => true,
]);
$I->canSeeAuthCredentials(false);
$I->wantTo('ensure, that jwt token is valid');
$jwt = $I->grabDataFromResponseByJsonPath('$.access_token')[0];
$I->amBearerAuthenticated($jwt);
$accountRoute = new AccountsRoute($I);
$accountRoute->get(5);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->notLoggedIn();
$I->wantTo('check, that password is really changed');
$authRoute->login('Notch', '12345678');
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace api\tests\functional;
use api\tests\_pages\AuthenticationRoute;
use api\tests\FunctionalTester;
class RefreshTokenCest {
public function testRefreshInvalidToken(FunctionalTester $I) {
$route = new AuthenticationRoute($I);
$I->wantTo('get error.refresh_token_not_exist if passed token is invalid');
$route->refreshToken('invalid-token');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'refresh_token' => 'error.refresh_token_not_exist',
],
]);
}
public function testRefreshToken(FunctionalTester $I) {
$route = new AuthenticationRoute($I);
$I->wantTo('get new access_token by my refresh_token');
$route->refreshToken('SOutIr6Seeaii3uqMVy3Wan8sKFVFrNz');
$I->canSeeResponseCodeIs(200);
$I->canSeeAuthCredentials(false);
}
}

View File

@@ -0,0 +1,299 @@
<?php
namespace api\tests\functional;
use Codeception\Example;
use api\tests\_pages\SignupRoute;
use api\tests\FunctionalTester;
class RegisterCest {
/**
* @var SignupRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new SignupRoute($I);
}
/**
* @dataProvider getSuccessInputExamples
*/
public function testUserCorrectRegistration(FunctionalTester $I, Example $example) {
$I->wantTo($example->offsetGet('case'));
$this->route->register($example->offsetGet('request'));
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson(['success' => true]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
}
/**
* @dataProvider getInvalidInputExamples
*/
public function testIncorrectRegistration(FunctionalTester $I, Example $example) {
$I->wantTo($example->offsetGet('case'));
$this->route->register($example->offsetGet('request'));
if ($example->offsetExists('canSee')) {
$I->canSeeResponseContainsJson($example->offsetGet('canSee'));
}
if ($example->offsetExists('cantSee')) {
$I->cantSeeResponseContainsJson($example->offsetGet('cantSee'));
}
if ($example->offsetExists('shouldNotMatch')) {
foreach ((array)$example->offsetGet('shouldNotMatch') as $jsonPath) {
$I->cantSeeResponseJsonMatchesJsonPath($jsonPath);
}
}
}
protected function getSuccessInputExamples(): array {
return [
[
'case' => 'ensure that signup works',
'request' => [
'username' => 'some_username',
'email' => 'some_email@example.com',
'password' => 'some_password',
'rePassword' => 'some_password',
'rulesAgreement' => true,
'lang' => 'ru',
],
],
[
'case' => 'ensure that signup allow reassign not finished registration username',
'request' => [
'username' => 'howe.garnett',
'email' => 'custom-email@gmail.com',
'password' => 'some_password',
'rePassword' => 'some_password',
'rulesAgreement' => true,
'lang' => 'ru',
],
],
[
'case' => 'ensure that signup allow reassign not finished registration email',
'request' => [
'username' => 'CustomUsername',
'email' => 'achristiansen@gmail.com',
'password' => 'some_password',
'rePassword' => 'some_password',
'rulesAgreement' => true,
'lang' => 'ru',
],
],
];
}
protected function getInvalidInputExamples(): array {
return [
[
'case' => 'get error.rulesAgreement_required if we don\'t accept rules',
'request' => [
'username' => 'ErickSkrauch',
'email' => 'erickskrauch@ely.by',
'password' => 'some_password',
'rePassword' => 'some_password',
],
'canSee' => [
'success' => false,
'errors' => [
'rulesAgreement' => 'error.rulesAgreement_required',
],
],
],
[
'case' => 'don\'t see error.rulesAgreement_requireds if we accept rules',
'request' => [
'rulesAgreement' => true,
],
'cantSee' => [
'errors' => [
'rulesAgreement' => 'error.rulesAgreement_required',
],
],
],
[
'case' => 'see error.username_required if username is not set',
'request' => [
'username' => '',
'email' => '',
'password' => '',
'rePassword' => '',
'rulesAgreement' => true,
],
'canSee' => [
'success' => false,
'errors' => [
'username' => 'error.username_required',
],
],
],
[
'case' => 'don\'t see error.username_required if username is not set',
'request' => [
'username' => 'valid_nickname',
'email' => '',
'password' => '',
'rePassword' => '',
'rulesAgreement' => true,
],
'cantSee' => [
'errors' => [
'username' => 'error.username_required',
],
],
],
[
'case' => 'see error.email_required if email is not set',
'request' => [
'username' => 'valid_nickname',
'email' => '',
'password' => '',
'rePassword' => '',
'rulesAgreement' => true,
],
'canSee' => [
'success' => false,
'errors' => [
'email' => 'error.email_required',
],
],
],
[
'case' => 'see error.email_invalid if email is set, but invalid',
'request' => [
'username' => 'valid_nickname',
'email' => 'invalid@email',
'password' => '',
'rePassword' => '',
'rulesAgreement' => true,
],
'canSee' => [
'success' => false,
'errors' => [
'email' => 'error.email_invalid',
],
],
],
[
'case' => 'see error.email_invalid if email is set, valid, but domain doesn\'t exist or don\'t have mx record',
'request' => [
'username' => 'valid_nickname',
'email' => 'invalid@this-should-be-really-no-exists-domain-63efd7ab-1529-46d5-9426-fa5ed9f710e6.com',
'password' => '',
'rePassword' => '',
'rulesAgreement' => true,
],
'canSee' => [
'success' => false,
'errors' => [
'email' => 'error.email_invalid',
],
],
],
[
'case' => 'see error.email_not_available if email is set, fully valid, but not available for registration',
'request' => [
'username' => 'valid_nickname',
'email' => 'admin@ely.by',
'password' => '',
'rePassword' => '',
'rulesAgreement' => true,
],
'canSee' => [
'success' => false,
'errors' => [
'email' => 'error.email_not_available',
],
],
],
[
'case' => 'don\'t see errors on email if email valid',
'request' => [
'username' => 'valid_nickname',
'email' => 'erickskrauch@ely.by',
'password' => '',
'rePassword' => '',
'rulesAgreement' => true,
],
'shouldNotMatch' => [
'$.errors.email',
],
],
[
'case' => 'see error.password_required if password is not set',
'request' => [
'username' => 'valid_nickname',
'email' => 'erickskrauch@ely.by',
'password' => '',
'rePassword' => '',
'rulesAgreement' => true,
],
'canSee' => [
'success' => false,
'errors' => [
'password' => 'error.password_required',
],
],
],
[
'case' => 'see error.password_too_short before it will be compared with rePassword',
'request' => [
'username' => 'valid_nickname',
'email' => 'correct-email@ely.by',
'password' => 'short',
'rePassword' => 'password',
'rulesAgreement' => true,
],
'canSee' => [
'success' => false,
'errors' => [
'password' => 'error.password_too_short',
],
],
'shouldNotMatch' => [
'$.errors.rePassword',
],
],
[
'case' => 'see error.rePassword_required if password valid and rePassword not set',
'request' => [
'username' => 'valid_nickname',
'email' => 'correct-email@ely.by',
'password' => 'valid-password',
'rePassword' => '',
'rulesAgreement' => true,
],
'canSee' => [
'success' => false,
'errors' => [
'rePassword' => 'error.rePassword_required',
],
],
],
[
'case' => 'see error.rePassword_does_not_match if password valid and rePassword doesn\'t match it',
'request' => [
'username' => 'valid_nickname',
'email' => 'correct-email@ely.by',
'password' => 'valid-password',
'rePassword' => 'password',
'rulesAgreement' => true,
],
'canSee' => [
'success' => false,
'errors' => [
'rePassword' => 'error.rePassword_does_not_match',
],
],
'shouldNotMatch' => [
'$.errors.password',
],
],
];
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace api\tests\functional;
use api\tests\_pages\SignupRoute;
use api\tests\FunctionalTester;
class RepeatAccountActivationCest {
public function testInvalidEmailOrAccountState(FunctionalTester $I) {
$route = new SignupRoute($I);
$I->wantTo('error.email_required on empty for submitting');
$route->sendRepeatMessage();
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'email' => 'error.email_required',
],
]);
$I->wantTo('error.email_not_found if email is not presented in db');
$route->sendRepeatMessage('im-not@exists.net');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'email' => 'error.email_not_found',
],
]);
$I->wantTo('error.account_already_activated if passed email matches with already activated account');
$route->sendRepeatMessage('admin@ely.by');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'email' => 'error.account_already_activated',
],
]);
$I->wantTo('error.recently_sent_message if last message was send too recently');
$route->sendRepeatMessage('achristiansen@gmail.com');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'email' => 'error.recently_sent_message',
],
]);
$I->canSeeResponseJsonMatchesJsonPath('$.data.canRepeatIn');
$I->canSeeResponseJsonMatchesJsonPath('$.data.repeatFrequency');
}
public function testSuccess(FunctionalTester $I) {
$route = new SignupRoute($I);
$I->wantTo('successfully resend account activation message');
$route->sendRepeatMessage('jon@ely.by');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson(['success' => true]);
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
}
}

View File

@@ -0,0 +1 @@
<?php

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace api\tests\functional\_steps;
use Ramsey\Uuid\Uuid;
use api\tests\_pages\AuthserverRoute;
use api\tests\FunctionalTester;
class AuthserverSteps extends FunctionalTester {
public function amAuthenticated(string $asUsername = 'admin', string $password = 'password_0'): array {
$route = new AuthserverRoute($this);
$clientToken = Uuid::uuid4()->toString();
$route->authenticate([
'username' => $asUsername,
'password' => $password,
'clientToken' => $clientToken,
]);
$accessToken = $this->grabDataFromResponseByJsonPath('$.accessToken')[0];
return [$accessToken, $clientToken];
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace api\tests\functional\_steps;
use api\components\OAuth2\Storage\ScopeStorage as S;
use api\tests\_pages\OauthRoute;
use api\tests\FunctionalTester;
class OauthSteps extends FunctionalTester {
public function getAuthCode(array $permissions = []) {
$this->amAuthenticated();
$route = new OauthRoute($this);
$route->complete([
'client_id' => 'ely',
'redirect_uri' => 'http://ely.by',
'response_type' => 'code',
'scope' => implode(',', $permissions),
], ['accept' => true]);
$this->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
$response = json_decode($this->grabResponse(), true);
preg_match('/code=([\w-]+)/', $response['redirectUri'], $matches);
return $matches[1];
}
public function getAccessToken(array $permissions = []) {
$authCode = $this->getAuthCode($permissions);
$response = $this->issueToken($authCode);
return $response['access_token'];
}
public function getRefreshToken(array $permissions = []) {
$authCode = $this->getAuthCode(array_merge([S::OFFLINE_ACCESS], $permissions));
$response = $this->issueToken($authCode);
return $response['refresh_token'];
}
public function issueToken($authCode) {
$route = new OauthRoute($this);
$route->issueToken([
'code' => $authCode,
'client_id' => 'ely',
'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
'redirect_uri' => 'http://ely.by',
'grant_type' => 'authorization_code',
]);
return json_decode($this->grabResponse(), true);
}
public function getAccessTokenByClientCredentialsGrant(array $permissions = [], $useTrusted = true) {
$route = new OauthRoute($this);
$route->issueToken([
'client_id' => $useTrusted ? 'trusted-client' : 'default-client',
'client_secret' => $useTrusted ? 'tXBbyvMcyaOgHMOAXBpN2EC7uFoJAaL9' : 'AzWRy7ZjS1yRQUk2vRBDic8fprOKDB1W',
'grant_type' => 'client_credentials',
'scope' => implode(',', $permissions),
]);
$response = json_decode($this->grabResponse(), true);
return $response['access_token'];
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace api\tests\functional\_steps;
use common\rbac\Permissions as P;
use Faker\Provider\Uuid;
use api\tests\_pages\SessionServerRoute;
use api\tests\FunctionalTester;
class SessionServerSteps extends FunctionalTester {
public function amJoined($byLegacy = false) {
$oauthSteps = new OauthSteps($this->scenario);
$accessToken = $oauthSteps->getAccessToken([P::MINECRAFT_SERVER_SESSION]);
$route = new SessionServerRoute($this);
$serverId = Uuid::uuid();
$username = 'Admin';
if ($byLegacy) {
$route->joinLegacy([
'sessionId' => 'token:' . $accessToken . ':' . 'df936908-b2e1-544d-96f8-2977ec213022',
'user' => $username,
'serverId' => $serverId,
]);
$this->canSeeResponseEquals('OK');
} else {
$route->join([
'accessToken' => $accessToken,
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
'serverId' => $serverId,
]);
$this->canSeeResponseContainsJson(['id' => 'OK']);
}
return [$username, $serverId];
}
public function canSeeValidTexturesResponse($expectedUsername, $expectedUuid) {
$this->seeResponseIsJson();
$this->canSeeResponseContainsJson([
'name' => $expectedUsername,
'id' => $expectedUuid,
'ely' => true,
'properties' => [
[
'name' => 'textures',
'signature' => 'Cg==',
],
],
]);
$this->canSeeResponseJsonMatchesJsonPath('$.properties[0].value');
$value = json_decode($this->grabResponse(), true)['properties'][0]['value'];
$decoded = json_decode(base64_decode($value), true);
$this->assertArrayHasKey('timestamp', $decoded);
$this->assertArrayHasKey('textures', $decoded);
$this->assertEquals($expectedUuid, $decoded['profileId']);
$this->assertEquals($expectedUsername, $decoded['profileName']);
$this->assertTrue($decoded['ely']);
$textures = $decoded['textures'];
$this->assertArrayHasKey('SKIN', $textures);
$skinTextures = $textures['SKIN'];
$this->assertArrayHasKey('url', $skinTextures);
$this->assertArrayHasKey('hash', $skinTextures);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace api\tests\functional\accounts;
use api\tests\_pages\AccountsRoute;
use api\tests\FunctionalTester;
class AcceptRulesCest {
/**
* @var AccountsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AccountsRoute($I);
}
public function testCurrent(FunctionalTester $I) {
$I->amAuthenticated('Veleyaba');
$this->route->acceptRules(9);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace api\tests\functional\accounts;
use common\rbac\Permissions as P;
use api\tests\_pages\AccountsRoute;
use api\tests\functional\_steps\OauthSteps;
use api\tests\FunctionalTester;
class BanCest {
/**
* @var AccountsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AccountsRoute($I);
}
public function testBanAccount(OauthSteps $I) {
$accessToken = $I->getAccessTokenByClientCredentialsGrant([P::BLOCK_ACCOUNT]);
$I->amBearerAuthenticated($accessToken);
$this->route->ban(1);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
public function testBanBannedAccount(OauthSteps $I) {
$accessToken = $I->getAccessTokenByClientCredentialsGrant([P::BLOCK_ACCOUNT]);
$I->amBearerAuthenticated($accessToken);
$this->route->ban(10);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'account' => 'error.account_already_banned',
],
]);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace api\tests\functional\accounts;
use api\tests\_pages\AccountsRoute;
use api\tests\FunctionalTester;
class ChangeEmailConfirmNewEmailCest {
/**
* @var AccountsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AccountsRoute($I);
}
public function testConfirmNewEmail(FunctionalTester $I) {
$I->wantTo('change my email and get changed value');
$I->amAuthenticated('CrafterGameplays');
$this->route->changeEmail(8, 'H28HBDCHHAG2HGHGHS');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
'data' => [
'email' => 'my-new-email@ely.by',
],
]);
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace api\tests\functional\accounts;
use api\tests\_pages\AccountsRoute;
use api\tests\FunctionalTester;
class ChangeEmailInitializeCest {
/**
* @var AccountsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AccountsRoute($I);
}
public function testChangeEmailInitialize(FunctionalTester $I) {
$I->wantTo('send current email confirmation');
$id = $I->amAuthenticated();
$this->route->changeEmailInitialize($id, 'password_0');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
public function testChangeEmailInitializeFrequencyError(FunctionalTester $I) {
$I->wantTo('see change email request frequency error');
$id = $I->amAuthenticated('ILLIMUNATI');
$this->route->changeEmailInitialize($id, 'password_0');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'email' => 'error.recently_sent_message',
],
]);
$I->canSeeResponseJsonMatchesJsonPath('$.data.canRepeatIn');
$I->canSeeResponseJsonMatchesJsonPath('$.data.repeatFrequency');
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace api\tests\functional\accounts;
use api\tests\_pages\AccountsRoute;
use api\tests\FunctionalTester;
use common\tests\helpers\Mock;
use yii\validators\EmailValidator;
class ChangeEmailSubmitNewEmailCest {
/**
* @var AccountsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AccountsRoute($I);
}
public function testSubmitNewEmail(FunctionalTester $I) {
Mock::func(EmailValidator::class, 'checkdnsrr')->andReturnTrue();
$I->wantTo('submit new email');
$id = $I->amAuthenticated('ILLIMUNATI');
$this->route->changeEmailSubmitNewEmail($id, 'H27HBDCHHAG2HGHGHS', 'my-new-email@ely.by');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace api\tests\functional\accounts;
use api\tests\_pages\AccountsRoute;
use api\tests\FunctionalTester;
class ChangeLangCest {
/**
* @var AccountsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AccountsRoute($I);
}
public function testSubmitNewEmail(FunctionalTester $I) {
$I->wantTo('change my account language');
$id = $I->amAuthenticated();
$this->route->changeLanguage($id, 'ru');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace api\tests\functional\accounts;
use common\models\Account;
use api\tests\_pages\AccountsRoute;
use api\tests\_pages\AuthenticationRoute;
use api\tests\functional\_steps\OauthSteps;
use api\tests\FunctionalTester;
class ChangePasswordCest {
/**
* @var AccountsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AccountsRoute($I);
}
public function _after() {
/** @var Account $account */
$account = Account::findOne(1);
$account->setPassword('password_0');
$account->save();
}
public function testChangePassword(FunctionalTester $I) {
$I->wantTo('change my password');
$id = $I->amAuthenticated();
$this->route->changePassword($id, 'password_0', 'new-password', 'new-password');
$this->assertSuccessResponse($I);
$I->notLoggedIn();
$loginRoute = new AuthenticationRoute($I);
$loginRoute->login('Admin', 'new-password');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
public function testChangePasswordInternal(OauthSteps $I) {
$accessToken = $I->getAccessTokenByClientCredentialsGrant(['change_account_password', 'escape_identity_verification']);
$I->amBearerAuthenticated($accessToken);
$this->route->changePassword(1, null, 'new-password-1', 'new-password-1');
$this->assertSuccessResponse($I);
}
private function assertSuccessResponse(FunctionalTester $I): void {
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace api\tests\functional\accounts;
use common\models\Account;
use api\tests\_pages\AccountsRoute;
use api\tests\functional\_steps\OauthSteps;
use api\tests\FunctionalTester;
class ChangeUsernameCest {
/**
* @var AccountsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AccountsRoute($I);
}
public function _after() {
/** @var Account $account */
$account = Account::findOne(1);
$account->username = 'Admin';
$account->save();
}
public function testChangeUsername(FunctionalTester $I) {
$I->wantTo('change my nickname');
$id = $I->amAuthenticated();
$this->route->changeUsername($id, 'password_0', 'bruce_wayne');
$this->assertSuccessResponse($I);
}
public function testChangeUsernameNotAvailable(FunctionalTester $I) {
$I->wantTo('see, that nickname "in use" is not available');
$id = $I->amAuthenticated();
$this->route->changeUsername($id, 'password_0', 'Jon');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'username' => 'error.username_not_available',
],
]);
}
public function testChangeUsernameInternal(OauthSteps $I) {
$accessToken = $I->getAccessTokenByClientCredentialsGrant(['change_account_username', 'escape_identity_verification']);
$I->amBearerAuthenticated($accessToken);
$this->route->changeUsername(1, null, 'im_batman');
$this->assertSuccessResponse($I);
}
/**
* @param FunctionalTester $I
*/
private function assertSuccessResponse(FunctionalTester $I): void {
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace api\tests\functional\accounts;
use OTPHP\TOTP;
use api\tests\_pages\AccountsRoute;
use api\tests\FunctionalTester;
class DisableTwoFactorAuthCest {
/**
* @var AccountsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AccountsRoute($I);
}
public function testFails(FunctionalTester $I) {
$accountId = $I->amAuthenticated('AccountWithEnabledOtp');
$this->route->disableTwoFactorAuth($accountId);
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'totp' => 'error.totp_required',
'password' => 'error.password_required',
],
]);
$this->route->disableTwoFactorAuth($accountId, '123456', 'invalid_password');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'totp' => 'error.totp_incorrect',
'password' => 'error.password_incorrect',
],
]);
$accountId = $I->amAuthenticated('AccountWithOtpSecret');
$this->route->disableTwoFactorAuth($accountId, '123456', 'invalid_password');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'account' => 'error.otp_not_enabled',
],
]);
}
public function testSuccessEnable(FunctionalTester $I) {
$accountId = $I->amAuthenticated('AccountWithEnabledOtp');
$totp = TOTP::create('BBBB');
$this->route->disableTwoFactorAuth($accountId, $totp->now(), 'password_0');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace api\tests\functional\accounts;
use OTPHP\TOTP;
use api\tests\_pages\AccountsRoute;
use api\tests\FunctionalTester;
class EnableTwoFactorAuthCest {
/**
* @var AccountsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AccountsRoute($I);
}
public function testFails(FunctionalTester $I) {
$accountId = $I->amAuthenticated('AccountWithOtpSecret');
$this->route->enableTwoFactorAuth($accountId);
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'totp' => 'error.totp_required',
'password' => 'error.password_required',
],
]);
$this->route->enableTwoFactorAuth($accountId, '123456', 'invalid_password');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'totp' => 'error.totp_incorrect',
'password' => 'error.password_incorrect',
],
]);
$accountId = $I->amAuthenticated('AccountWithEnabledOtp');
$this->route->enableTwoFactorAuth($accountId, '123456', 'invalid_password');
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'account' => 'error.otp_already_enabled',
],
]);
}
public function testSuccessEnable(FunctionalTester $I) {
$accountId = $I->amAuthenticated('AccountWithOtpSecret');
$totp = TOTP::create('AAAA');
$this->route->enableTwoFactorAuth($accountId, $totp->now(), 'password_0');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
public function testSuccessEnableWithNotSoExpiredCode(FunctionalTester $I) {
$accountId = $I->amAuthenticated('AccountWithOtpSecret');
$totp = TOTP::create('AAAA');
$this->route->enableTwoFactorAuth($accountId, $totp->at(time() - 35), 'password_0');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
}

View File

@@ -0,0 +1,114 @@
<?php
namespace api\tests\functional\accounts;
use api\tests\_pages\AccountsRoute;
use api\tests\FunctionalTester;
class GetCest {
/**
* @var AccountsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AccountsRoute($I);
}
public function testGetInfo(FunctionalTester $I) {
$accountId = $I->amAuthenticated();
$this->route->get($accountId);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'id' => 1,
'uuid' => 'df936908-b2e1-544d-96f8-2977ec213022',
'username' => 'Admin',
'isOtpEnabled' => false,
'email' => 'admin@ely.by',
'lang' => 'en',
'isActive' => true,
'hasMojangUsernameCollision' => false,
'shouldAcceptRules' => false,
'elyProfileLink' => 'http://ely.by/u1',
]);
$I->canSeeResponseJsonMatchesJsonPath('$.passwordChangedAt');
}
public function testGetInfoAboutCurrentUser(FunctionalTester $I) {
$I->wantTo('get info about user with 0 id, e.g. current');
$I->amAuthenticated();
$this->route->get(0);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'id' => 1,
'uuid' => 'df936908-b2e1-544d-96f8-2977ec213022',
'username' => 'Admin',
'isOtpEnabled' => false,
'email' => 'admin@ely.by',
'lang' => 'en',
'isActive' => true,
'hasMojangUsernameCollision' => false,
'shouldAcceptRules' => false,
'elyProfileLink' => 'http://ely.by/u1',
]);
$I->canSeeResponseJsonMatchesJsonPath('$.passwordChangedAt');
}
public function testGetWithNotAcceptedLatestRules(FunctionalTester $I) {
$accountId = $I->amAuthenticated('Veleyaba');
$this->route->get($accountId);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'id' => 9,
'uuid' => '410462d3-8e71-47cc-bac6-64f77f88cf80',
'username' => 'Veleyaba',
'email' => 'veleyaba@gmail.com',
'isOtpEnabled' => false,
'lang' => 'en',
'isActive' => true,
'hasMojangUsernameCollision' => false,
'shouldAcceptRules' => true,
'elyProfileLink' => 'http://ely.by/u9',
]);
$I->canSeeResponseJsonMatchesJsonPath('$.passwordChangedAt');
}
public function testGetInfoWithExpiredToken(FunctionalTester $I) {
// Устанавливаем заведомо истёкший токен
$I->amBearerAuthenticated(
'eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0NjQ2Mjc1NDUsImV4cCI6MTQ2NDYzMTE0NSwic3ViIjoiZWx5fDEiLCJlbHktc' .
'2NvcGVzIjoiYWNjb3VudHNfd2ViX3VzZXIifQ.v1u8V5wk2RkWmnZtH3jZvM3zO1Gpgbp2DQFfLfy8jHY'
);
$this->route->get(1);
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'name' => 'Unauthorized',
'message' => 'Token expired',
'code' => 0,
'status' => 401,
]);
}
public function testGetInfoNotCurrentAccount(FunctionalTester $I) {
$I->amAuthenticated();
$this->route->get(10);
$I->canSeeResponseCodeIs(403);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'name' => 'Forbidden',
'message' => 'You are not allowed to perform this action.',
'code' => 0,
'status' => 403,
]);
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace api\tests\functional\accounts;
use common\rbac\Permissions as P;
use api\tests\_pages\AccountsRoute;
use api\tests\functional\_steps\OauthSteps;
use api\tests\FunctionalTester;
class PardonCest {
/**
* @var AccountsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AccountsRoute($I);
}
public function testPardonAccount(OauthSteps $I) {
$accessToken = $I->getAccessTokenByClientCredentialsGrant([P::BLOCK_ACCOUNT]);
$I->amBearerAuthenticated($accessToken);
$this->route->pardon(10);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
public function testPardonNotBannedAccount(OauthSteps $I) {
$accessToken = $I->getAccessTokenByClientCredentialsGrant([P::BLOCK_ACCOUNT]);
$I->amBearerAuthenticated($accessToken);
$this->route->pardon(1);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'account' => 'error.account_not_banned',
],
]);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace api\tests\functional\accounts;
use api\tests\_pages\AccountsRoute;
use api\tests\FunctionalTester;
class TwoFactorAuthCredentialsCest {
/**
* @var AccountsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AccountsRoute($I);
}
public function testGetCredentials(FunctionalTester $I) {
$accountId = $I->amAuthenticated();
$this->route->getTwoFactorAuthCredentials($accountId);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseJsonMatchesJsonPath('$.secret');
$I->canSeeResponseJsonMatchesJsonPath('$.uri');
$I->canSeeResponseJsonMatchesJsonPath('$.qr');
}
}

View File

@@ -0,0 +1,158 @@
<?php
namespace api\tests\functional\authserver;
use Ramsey\Uuid\Uuid;
use api\tests\_pages\AuthserverRoute;
use api\tests\FunctionalTester;
class AuthorizationCest {
/**
* @var AuthserverRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AuthserverRoute($I);
}
public function byName(FunctionalTester $I) {
$I->wantTo('authenticate by username and password');
$this->route->authenticate([
'username' => 'admin',
'password' => 'password_0',
'clientToken' => Uuid::uuid4()->toString(),
]);
$this->testSuccessResponse($I);
}
public function byEmail(FunctionalTester $I) {
$I->wantTo('authenticate by email and password');
$this->route->authenticate([
'username' => 'admin@ely.by',
'password' => 'password_0',
'clientToken' => Uuid::uuid4()->toString(),
]);
$this->testSuccessResponse($I);
}
public function byNamePassedViaPOSTBody(FunctionalTester $I) {
$I->wantTo('authenticate by username and password sent via post body');
$this->route->authenticate(json_encode([
'username' => 'admin',
'password' => 'password_0',
'clientToken' => Uuid::uuid4()->toString(),
]));
$this->testSuccessResponse($I);
}
public function byEmailWithEnabledTwoFactorAuth(FunctionalTester $I) {
$I->wantTo('get valid error by authenticate account with enabled two factor auth');
$this->route->authenticate([
'username' => 'otp@gmail.com',
'password' => 'password_0',
'clientToken' => Uuid::uuid4()->toString(),
]);
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'ForbiddenOperationException',
'errorMessage' => 'Account protected with two factor auth.',
]);
}
public function byEmailWithParamsAsJsonInPostBody(FunctionalTester $I) {
$I->wantTo('authenticate by email and password, passing values as serialized string in post body');
$this->route->authenticate(json_encode([
'username' => 'admin@ely.by',
'password' => 'password_0',
'clientToken' => Uuid::uuid4()->toString(),
]));
$this->testSuccessResponse($I);
}
public function longClientToken(FunctionalTester $I) {
$I->wantTo('send non uuid clientToken, but less then 255 characters');
$this->route->authenticate([
'username' => 'admin@ely.by',
'password' => 'password_0',
'clientToken' => str_pad('', 255, 'x'),
]);
$this->testSuccessResponse($I);
}
public function tooLongClientToken(FunctionalTester $I) {
$I->wantTo('send non uuid clientToken with more then 255 characters length');
$this->route->authenticate([
'username' => 'admin@ely.by',
'password' => 'password_0',
'clientToken' => str_pad('', 256, 'x'),
]);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'IllegalArgumentException',
'errorMessage' => 'clientToken is too long.',
]);
}
public function wrongArguments(FunctionalTester $I) {
$I->wantTo('get error on wrong amount of arguments');
$this->route->authenticate([
'key' => 'value',
]);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'IllegalArgumentException',
'errorMessage' => 'credentials can not be null.',
]);
}
public function wrongNicknameAndPassword(FunctionalTester $I) {
$I->wantTo('authenticate by username and password with wrong data');
$this->route->authenticate([
'username' => 'nonexistent_user',
'password' => 'nonexistent_password',
'clientToken' => Uuid::uuid4()->toString(),
]);
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'ForbiddenOperationException',
'errorMessage' => 'Invalid credentials. Invalid nickname or password.',
]);
}
public function bannedAccount(FunctionalTester $I) {
$I->wantTo('authenticate in suspended account');
$this->route->authenticate([
'username' => 'Banned',
'password' => 'password_0',
'clientToken' => Uuid::uuid4()->toString(),
]);
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseContainsJson([
'error' => 'ForbiddenOperationException',
'errorMessage' => 'This account has been suspended.',
]);
}
private function testSuccessResponse(FunctionalTester $I) {
$I->seeResponseCodeIs(200);
$I->seeResponseIsJson();
$I->canSeeResponseJsonMatchesJsonPath('$.accessToken');
$I->canSeeResponseJsonMatchesJsonPath('$.clientToken');
$I->canSeeResponseJsonMatchesJsonPath('$.availableProfiles[0].id');
$I->canSeeResponseJsonMatchesJsonPath('$.availableProfiles[0].name');
$I->canSeeResponseJsonMatchesJsonPath('$.availableProfiles[0].legacy');
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.id');
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.name');
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.legacy');
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace api\tests\functional\authserver;
use Ramsey\Uuid\Uuid;
use api\tests\_pages\AuthserverRoute;
use api\tests\functional\_steps\AuthserverSteps;
class InvalidateCest {
/**
* @var AuthserverRoute
*/
private $route;
public function _before(AuthserverSteps $I) {
$this->route = new AuthserverRoute($I);
}
public function invalidate(AuthserverSteps $I) {
$I->wantTo('invalidate my token');
[$accessToken, $clientToken] = $I->amAuthenticated();
$this->route->invalidate([
'accessToken' => $accessToken,
'clientToken' => $clientToken,
]);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseEquals('');
}
public function wrongArguments(AuthserverSteps $I) {
$I->wantTo('get error on wrong amount of arguments');
$this->route->invalidate([
'key' => 'value',
]);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'IllegalArgumentException',
'errorMessage' => 'credentials can not be null.',
]);
}
public function wrongAccessTokenOrClientToken(AuthserverSteps $I) {
$I->wantTo('invalidate by wrong client and access token');
$this->route->invalidate([
'accessToken' => Uuid::uuid4()->toString(),
'clientToken' => Uuid::uuid4()->toString(),
]);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseEquals('');
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace api\tests\functional\authserver;
use Ramsey\Uuid\Uuid;
use api\tests\_pages\AuthserverRoute;
use api\tests\functional\_steps\AuthserverSteps;
class RefreshCest {
/**
* @var AuthserverRoute
*/
private $route;
public function _before(AuthserverSteps $I) {
$this->route = new AuthserverRoute($I);
}
public function refresh(AuthserverSteps $I) {
$I->wantTo('refresh my accessToken');
[$accessToken, $clientToken] = $I->amAuthenticated();
$this->route->refresh([
'accessToken' => $accessToken,
'clientToken' => $clientToken,
]);
$I->seeResponseCodeIs(200);
$I->seeResponseIsJson();
$I->canSeeResponseJsonMatchesJsonPath('$.accessToken');
$I->canSeeResponseJsonMatchesJsonPath('$.clientToken');
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.id');
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.name');
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.legacy');
$I->cantSeeResponseJsonMatchesJsonPath('$.availableProfiles');
}
public function wrongArguments(AuthserverSteps $I) {
$I->wantTo('get error on wrong amount of arguments');
$this->route->refresh([
'key' => 'value',
]);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'IllegalArgumentException',
'errorMessage' => 'credentials can not be null.',
]);
}
public function wrongAccessToken(AuthserverSteps $I) {
$I->wantTo('get error on wrong access or client tokens');
$this->route->refresh([
'accessToken' => Uuid::uuid4()->toString(),
'clientToken' => Uuid::uuid4()->toString(),
]);
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'ForbiddenOperationException',
'errorMessage' => 'Invalid token.',
]);
}
public function refreshTokenFromBannedUser(AuthserverSteps $I) {
$I->wantTo('refresh token from suspended account');
$this->route->refresh([
'accessToken' => '918ecb41-616c-40ee-a7d2-0b0ef0d0d732',
'clientToken' => '6042634a-a1e2-4aed-866c-c661fe4e63e2',
]);
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseContainsJson([
'error' => 'ForbiddenOperationException',
'errorMessage' => 'This account has been suspended.',
]);
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace api\tests\functional\authserver;
use api\tests\_pages\AuthserverRoute;
use api\tests\functional\_steps\AuthserverSteps;
class SignoutCest {
/**
* @var AuthserverRoute
*/
private $route;
public function _before(AuthserverSteps $I) {
$this->route = new AuthserverRoute($I);
}
public function byName(AuthserverSteps $I) {
$I->wantTo('signout by nickname and password');
$this->route->signout([
'username' => 'admin',
'password' => 'password_0',
]);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseEquals('');
}
public function byEmail(AuthserverSteps $I) {
$I->wantTo('signout by email and password');
$this->route->signout([
'username' => 'admin@ely.by',
'password' => 'password_0',
]);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseEquals('');
}
public function wrongArguments(AuthserverSteps $I) {
$I->wantTo('get error on wrong amount of arguments');
$this->route->signout([
'key' => 'value',
]);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'IllegalArgumentException',
'errorMessage' => 'credentials can not be null.',
]);
}
public function wrongNicknameAndPassword(AuthserverSteps $I) {
$I->wantTo('signout by nickname and password with wrong data');
$this->route->signout([
'username' => 'nonexistent_user',
'password' => 'nonexistent_password',
]);
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'ForbiddenOperationException',
'errorMessage' => 'Invalid credentials. Invalid nickname or password.',
]);
}
public function bannedAccount(AuthserverSteps $I) {
$I->wantTo('signout from banned account');
$this->route->signout([
'username' => 'Banned',
'password' => 'password_0',
]);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseEquals('');
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace api\tests\functional\authserver;
use Ramsey\Uuid\Uuid;
use api\tests\_pages\AuthserverRoute;
use api\tests\functional\_steps\AuthserverSteps;
class ValidateCest {
/**
* @var AuthserverRoute
*/
private $route;
public function _before(AuthserverSteps $I) {
$this->route = new AuthserverRoute($I);
}
public function validate(AuthserverSteps $I) {
$I->wantTo('validate my accessToken');
[$accessToken] = $I->amAuthenticated();
$this->route->validate([
'accessToken' => $accessToken,
]);
$I->seeResponseCodeIs(200);
$I->canSeeResponseEquals('');
}
public function wrongArguments(AuthserverSteps $I) {
$I->wantTo('get error on wrong amount of arguments');
$this->route->validate([
'key' => 'value',
]);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'IllegalArgumentException',
'errorMessage' => 'credentials can not be null.',
]);
}
public function wrongAccessToken(AuthserverSteps $I) {
$I->wantTo('get error on wrong accessToken');
$this->route->validate([
'accessToken' => Uuid::uuid4()->toString(),
]);
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'ForbiddenOperationException',
'errorMessage' => 'Invalid token.',
]);
}
public function expiredAccessToken(AuthserverSteps $I) {
$I->wantTo('get error on expired accessToken');
$this->route->validate([
// Заведомо истёкший токен из дампа
'accessToken' => '6042634a-a1e2-4aed-866c-c661fe4e63e2',
]);
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'ForbiddenOperationException',
'errorMessage' => 'Token expired.',
]);
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace api\tests\functional\internal;
use api\tests\_pages\InternalRoute;
use api\tests\functional\_steps\OauthSteps;
use api\tests\FunctionalTester;
class InfoCest {
/**
* @var InternalRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new InternalRoute($I);
}
public function testGetInfoById(OauthSteps $I) {
$accessToken = $I->getAccessTokenByClientCredentialsGrant(['internal_account_info']);
$I->amBearerAuthenticated($accessToken);
$this->route->info('id', 1);
$this->expectSuccessResponse($I);
}
public function testGetInfoByUuid(OauthSteps $I) {
$accessToken = $I->getAccessTokenByClientCredentialsGrant(['internal_account_info']);
$I->amBearerAuthenticated($accessToken);
$this->route->info('uuid', 'df936908-b2e1-544d-96f8-2977ec213022');
$this->expectSuccessResponse($I);
}
public function testGetInfoByUsername(OauthSteps $I) {
$accessToken = $I->getAccessTokenByClientCredentialsGrant(['internal_account_info']);
$I->amBearerAuthenticated($accessToken);
$this->route->info('username', 'admin');
$this->expectSuccessResponse($I);
}
public function testInvalidParams(OauthSteps $I) {
$accessToken = $I->getAccessTokenByClientCredentialsGrant(['internal_account_info']);
$I->amBearerAuthenticated($accessToken);
$this->route->info('', '');
$I->canSeeResponseCodeIs(400);
}
public function testAccountNotFound(OauthSteps $I) {
$accessToken = $I->getAccessTokenByClientCredentialsGrant(['internal_account_info']);
$I->amBearerAuthenticated($accessToken);
$this->route->info('username', 'this-user-not-exists');
$I->canSeeResponseCodeIs(404);
}
/**
* @param OauthSteps $I
*/
private function expectSuccessResponse(OauthSteps $I): void {
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'id' => 1,
'uuid' => 'df936908-b2e1-544d-96f8-2977ec213022',
'email' => 'admin@ely.by',
'username' => 'Admin',
]);
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace api\tests\functional\authserver;
use api\tests\_pages\MojangApiRoute;
use api\tests\FunctionalTester;
class UsernameToUuidCest {
/**
* @var MojangApiRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new MojangApiRoute($I);
}
public function getUuidByUsername(FunctionalTester $I) {
$I->wantTo('get user uuid by his username');
$this->route->usernameToUuid('Admin');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'id' => 'df936908b2e1544d96f82977ec213022',
'name' => 'Admin',
]);
}
public function getUuidByUsernameAtMoment(FunctionalTester $I) {
$I->wantTo('get user uuid by his username at fixed moment');
$this->route->usernameToUuid('klik201', 1474404142);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'id' => 'd6b3e93564664cb886dbb5df91ae6541',
'name' => 'klik202',
]);
}
public function getUuidByUsernameAtWrongMoment(FunctionalTester $I) {
$I->wantTo('get 204 if passed once used, but changed username at moment, when it was changed');
$this->route->usernameToUuid('klik201', 1474404144);
$I->canSeeResponseCodeIs(204);
$I->canSeeResponseEquals('');
}
public function getUuidByUsernameWithoutMoment(FunctionalTester $I) {
$I->wantTo('get 204 if username not busy and not passed valid time mark, when it was busy');
$this->route->usernameToUuid('klik201');
$I->canSeeResponseCodeIs(204);
$I->canSeeResponseEquals('');
}
public function getUuidByWrongUsername(FunctionalTester $I) {
$I->wantTo('get user uuid by some wrong username');
$this->route->usernameToUuid('not-exists-user');
$I->canSeeResponseCodeIs(204);
$I->canSeeResponseEquals('');
}
public function nonPassedUsername(FunctionalTester $I) {
$I->wantTo('get 404 on not passed username');
$this->route->usernameToUuid('');
$I->canSeeResponseCodeIs(404);
}
}

View File

@@ -0,0 +1,121 @@
<?php
namespace api\tests\functional\authserver;
use api\tests\_pages\MojangApiRoute;
use api\tests\FunctionalTester;
class UsernamesToUuidsCest {
/**
* @var MojangApiRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new MojangApiRoute($I);
}
public function geUuidByOneUsername(FunctionalTester $I) {
$I->wantTo('get uuid by one username');
$this->route->uuidsByUsernames(['Admin']);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
[
'id' => 'df936908b2e1544d96f82977ec213022',
'name' => 'Admin',
],
]);
}
public function getUuidsByUsernames(FunctionalTester $I) {
$I->wantTo('get uuids by few usernames');
$this->route->uuidsByUsernames(['Admin', 'AccWithOldPassword', 'Notch']);
$this->validateFewValidUsernames($I);
}
public function getUuidsByUsernamesWithPostString(FunctionalTester $I) {
$I->wantTo('get uuids by few usernames');
$this->route->uuidsByUsernames(json_encode(['Admin', 'AccWithOldPassword', 'Notch']));
$this->validateFewValidUsernames($I);
}
public function getUuidsByPartialNonexistentUsernames(FunctionalTester $I) {
$I->wantTo('get uuids by few usernames and some nonexistent');
$this->route->uuidsByUsernames(['Admin', 'not-exists-user']);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
[
'id' => 'df936908b2e1544d96f82977ec213022',
'name' => 'Admin',
],
]);
}
public function passAllNonexistentUsernames(FunctionalTester $I) {
$I->wantTo('get specific response when pass all nonexistent usernames');
$this->route->uuidsByUsernames(['not-exists-1', 'not-exists-2']);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([]);
}
public function passTooManyUsernames(FunctionalTester $I) {
$I->wantTo('get specific response when pass too many usernames');
$usernames = [];
for ($i = 0; $i < 150; $i++) {
$usernames[] = random_bytes(10);
}
$this->route->uuidsByUsernames($usernames);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'IllegalArgumentException',
'errorMessage' => 'Not more that 100 profile name per call is allowed.',
]);
}
public function passEmptyUsername(FunctionalTester $I) {
$I->wantTo('get specific response when pass empty username');
$this->route->uuidsByUsernames(['Admin', '']);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'IllegalArgumentException',
'errorMessage' => 'profileName can not be null, empty or array key.',
]);
}
public function passEmptyField(FunctionalTester $I) {
$I->wantTo('get response when pass empty array');
$this->route->uuidsByUsernames([]);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'IllegalArgumentException',
'errorMessage' => 'Passed array of profile names is an invalid JSON string.',
]);
}
private function validateFewValidUsernames(FunctionalTester $I) {
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
[
'id' => 'df936908b2e1544d96f82977ec213022',
'name' => 'Admin',
],
[
'id' => 'bdc239f08a22518d8b93f02d4827c3eb',
'name' => 'AccWithOldPassword',
],
[
'id' => '4aaf4f003b5b4d3692529e8ee0c86679',
'name' => 'Notch',
],
]);
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace api\tests\functional\authserver;
use Faker\Provider\Uuid;
use api\tests\_pages\MojangApiRoute;
use api\tests\FunctionalTester;
class UuidToUsernamesHistoryCest {
/**
* @var MojangApiRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new MojangApiRoute($I);
}
public function getUsernameByUuid(FunctionalTester $I) {
$I->wantTo('get usernames history by uuid for user, without history');
$this->route->usernamesByUuid('df936908b2e1544d96f82977ec213022');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
[
'name' => 'Admin',
],
]);
}
public function getUsernameByUuidWithHistory(FunctionalTester $I) {
$I->wantTo('get usernames history by dashed uuid and expect history with time marks');
$this->route->usernamesByUuid('d6b3e935-6466-4cb8-86db-b5df91ae6541');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
[
'name' => 'klik202',
],
[
'name' => 'klik201',
'changedToAt' => 1474404141000,
],
[
'name' => 'klik202',
'changedToAt' => 1474404143000,
],
]);
}
public function passWrongUuid(FunctionalTester $I) {
$I->wantTo('get user username by some wrong uuid');
$this->route->usernamesByUuid(Uuid::uuid());
$I->canSeeResponseCodeIs(204);
$I->canSeeResponseEquals('');
}
public function passWrongUuidFormat(FunctionalTester $I) {
$I->wantTo('call profile route with invalid uuid string');
$this->route->usernamesByUuid('bla-bla-bla');
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'IllegalArgumentException',
'errorMessage' => 'Invalid uuid format.',
]);
}
}

View File

@@ -0,0 +1,113 @@
<?php
namespace api\tests\functional\oauth;
use api\tests\_pages\OauthRoute;
use api\tests\functional\_steps\OauthSteps;
use api\tests\FunctionalTester;
class AccessTokenCest {
/**
* @var OauthRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new OauthRoute($I);
}
public function testIssueTokenWithWrongArgs(OauthSteps $I) {
$I->wantTo('check behavior on on request without any credentials');
$this->route->issueToken();
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseContainsJson([
'error' => 'invalid_request',
'message' => 'The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the "grant_type" parameter.',
]);
$I->wantTo('check behavior on passing invalid auth code');
$this->route->issueToken($this->buildParams(
'wrong-auth-code',
'ely',
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
'http://ely.by'
));
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseContainsJson([
'error' => 'invalid_request',
'message' => 'The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the "code" parameter.',
]);
$authCode = $I->getAuthCode();
$I->wantTo('check behavior on passing invalid redirect_uri');
$this->route->issueToken($this->buildParams(
$authCode,
'ely',
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
'http://some-other.domain'
));
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseContainsJson([
'error' => 'invalid_client',
'message' => 'Client authentication failed.',
]);
}
public function testIssueToken(OauthSteps $I) {
$authCode = $I->getAuthCode();
$this->route->issueToken($this->buildParams(
$authCode,
'ely',
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
'http://ely.by'
));
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'token_type' => 'Bearer',
]);
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
$I->cantSeeResponseJsonMatchesJsonPath('$.refresh_token');
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
}
public function testIssueTokenWithRefreshToken(OauthSteps $I) {
$authCode = $I->getAuthCode(['offline_access']);
$this->route->issueToken($this->buildParams(
$authCode,
'ely',
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
'http://ely.by'
));
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'token_type' => 'Bearer',
]);
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
$I->canSeeResponseJsonMatchesJsonPath('$.refresh_token');
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
}
private function buildParams($code = null, $clientId = null, $clientSecret = null, $redirectUri = null) {
$params = ['grant_type' => 'authorization_code'];
if ($code !== null) {
$params['code'] = $code;
}
if ($clientId !== null) {
$params['client_id'] = $clientId;
}
if ($clientSecret !== null) {
$params['client_secret'] = $clientSecret;
}
if ($redirectUri !== null) {
$params['redirect_uri'] = $redirectUri;
}
return $params;
}
}

View File

@@ -0,0 +1,304 @@
<?php
namespace api\tests\functional\oauth;
use common\rbac\Permissions as P;
use api\tests\_pages\OauthRoute;
use api\tests\FunctionalTester;
class AuthCodeCest {
/**
* @var OauthRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new OauthRoute($I);
}
public function testValidateRequest(FunctionalTester $I) {
$this->testOauthParamsValidation($I, 'validate');
$I->wantTo('validate and obtain information about new auth request');
$this->route->validate($this->buildQueryParams(
'ely',
'http://ely.by',
'code',
[P::MINECRAFT_SERVER_SESSION, 'account_info', 'account_email'],
'test-state'
));
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
'oAuth' => [
'client_id' => 'ely',
'redirect_uri' => 'http://ely.by',
'response_type' => 'code',
'scope' => 'minecraft_server_session,account_info,account_email',
'state' => 'test-state',
],
'client' => [
'id' => 'ely',
'name' => 'Ely.by',
'description' => 'Всем знакомое елуби',
],
'session' => [
'scopes' => [
'minecraft_server_session',
'account_info',
'account_email',
],
],
]);
}
public function testValidateWithDescriptionReplaceRequest(FunctionalTester $I) {
$I->amAuthenticated();
$I->wantTo('validate and get information with description replacement');
$this->route->validate($this->buildQueryParams(
'ely',
'http://ely.by',
'code',
null,
null,
[
'description' => 'all familiar eliby',
]
));
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'client' => [
'description' => 'all familiar eliby',
],
]);
}
public function testCompleteValidationAction(FunctionalTester $I) {
$I->amAuthenticated();
$I->wantTo('validate all oAuth params on complete request');
$this->testOauthParamsValidation($I, 'complete');
}
public function testCompleteActionOnWrongConditions(FunctionalTester $I) {
$I->amAuthenticated();
$I->wantTo('get accept_required if I don\'t require any scope, but this is first time request');
$this->route->complete($this->buildQueryParams(
'ely',
'http://ely.by',
'code'
));
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseContainsJson([
'success' => false,
'error' => 'accept_required',
'parameter' => '',
'statusCode' => 401,
]);
$I->wantTo('get accept_required if I require some scopes on first time');
$this->route->complete($this->buildQueryParams(
'ely',
'http://ely.by',
'code',
[P::MINECRAFT_SERVER_SESSION]
));
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseContainsJson([
'success' => false,
'error' => 'accept_required',
'parameter' => '',
'statusCode' => 401,
]);
}
public function testCompleteActionSuccess(FunctionalTester $I) {
$I->amAuthenticated();
$I->wantTo('get auth code if I require some scope and pass accept field');
$this->route->complete($this->buildQueryParams(
'ely',
'http://ely.by',
'code',
[P::MINECRAFT_SERVER_SESSION]
), ['accept' => true]);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseContainsJson([
'success' => true,
]);
$I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
$I->wantTo('get auth code if I don\'t require any scope and don\'t pass accept field, but previously have ' .
'successful request');
$this->route->complete($this->buildQueryParams(
'ely',
'http://ely.by',
'code'
));
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseContainsJson([
'success' => true,
]);
$I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
$I->wantTo('get auth code if I require some scopes and don\'t pass accept field, but previously have successful ' .
'request with same scopes');
$this->route->complete($this->buildQueryParams(
'ely',
'http://ely.by',
'code',
[P::MINECRAFT_SERVER_SESSION]
));
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseContainsJson([
'success' => true,
]);
$I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
}
public function testAcceptRequiredOnNewScope(FunctionalTester $I) {
$I->amAuthenticated();
$I->wantTo('get accept_required if I have previous successful request, but now require some new scope');
$this->route->complete($this->buildQueryParams(
'ely',
'http://ely.by',
'code',
[P::MINECRAFT_SERVER_SESSION]
), ['accept' => true]);
$this->route->complete($this->buildQueryParams(
'ely',
'http://ely.by',
'code',
[P::MINECRAFT_SERVER_SESSION, 'account_info']
));
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseContainsJson([
'success' => false,
'error' => 'accept_required',
'parameter' => '',
'statusCode' => 401,
]);
}
public function testCompleteActionWithDismissState(FunctionalTester $I) {
$I->amAuthenticated();
$I->wantTo('get access_denied error if I pass accept in false state');
$this->route->complete($this->buildQueryParams(
'ely',
'http://ely.by',
'code',
[P::MINECRAFT_SERVER_SESSION]
), ['accept' => false]);
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseContainsJson([
'success' => false,
'error' => 'access_denied',
'parameter' => '',
'statusCode' => 401,
]);
$I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
}
private function buildQueryParams(
$clientId = null,
$redirectUri = null,
$responseType = null,
$scopes = [],
$state = null,
$customData = []
) {
$params = $customData;
if ($clientId !== null) {
$params['client_id'] = $clientId;
}
if ($redirectUri !== null) {
$params['redirect_uri'] = $redirectUri;
}
if ($responseType !== null) {
$params['response_type'] = $responseType;
}
if ($state !== null) {
$params['state'] = $state;
}
if (!empty($scopes)) {
if (is_array($scopes)) {
$scopes = implode(',', $scopes);
}
$params['scope'] = $scopes;
}
return $params;
}
private function testOauthParamsValidation(FunctionalTester $I, $action) {
$I->wantTo('check behavior on invalid request without one or few params');
$this->route->$action($this->buildQueryParams());
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => false,
'error' => 'invalid_request',
'parameter' => 'client_id',
'statusCode' => 400,
]);
$I->wantTo('check behavior on invalid client id');
$this->route->$action($this->buildQueryParams('non-exists-client', 'http://some-resource.by', 'code'));
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => false,
'error' => 'invalid_client',
'statusCode' => 401,
]);
$I->wantTo('check behavior on invalid response type');
$this->route->$action($this->buildQueryParams('ely', 'http://ely.by', 'kitty'));
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => false,
'error' => 'unsupported_response_type',
'parameter' => 'kitty',
'statusCode' => 400,
]);
$I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
$I->wantTo('check behavior on some invalid scopes');
$this->route->$action($this->buildQueryParams('ely', 'http://ely.by', 'code', [
P::MINECRAFT_SERVER_SESSION,
'some_wrong_scope',
]));
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => false,
'error' => 'invalid_scope',
'parameter' => 'some_wrong_scope',
'statusCode' => 400,
]);
$I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
$I->wantTo('check behavior on request internal scope');
$this->route->$action($this->buildQueryParams('ely', 'http://ely.by', 'code', [
P::MINECRAFT_SERVER_SESSION,
P::BLOCK_ACCOUNT,
]));
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => false,
'error' => 'invalid_scope',
'parameter' => P::BLOCK_ACCOUNT,
'statusCode' => 400,
]);
$I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
}
}

View File

@@ -0,0 +1,120 @@
<?php
namespace api\tests\functional\oauth;
use api\tests\_pages\OauthRoute;
use api\tests\functional\_steps\OauthSteps;
use api\tests\FunctionalTester;
class ClientCredentialsCest {
/**
* @var OauthRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new OauthRoute($I);
}
public function testIssueTokenWithWrongArgs(FunctionalTester $I) {
$I->wantTo('check behavior on on request without any credentials');
$this->route->issueToken($this->buildParams());
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseContainsJson([
'error' => 'invalid_request',
]);
$I->wantTo('check behavior on passing invalid client_id');
$this->route->issueToken($this->buildParams(
'invalid-client',
'invalid-secret',
['invalid-scope']
));
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseContainsJson([
'error' => 'invalid_client',
]);
$I->wantTo('check behavior on passing invalid client_secret');
$this->route->issueToken($this->buildParams(
'ely',
'invalid-secret',
['invalid-scope']
));
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseContainsJson([
'error' => 'invalid_client',
]);
$I->wantTo('check behavior on passing invalid client_secret');
$this->route->issueToken($this->buildParams(
'ely',
'invalid-secret',
['invalid-scope']
));
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseContainsJson([
'error' => 'invalid_client',
]);
}
public function testIssueTokenWithPublicScopes(OauthSteps $I) {
// TODO: у нас пока нет публичных скоупов, поэтому тест прогоняется с пустым набором
$this->route->issueToken($this->buildParams(
'ely',
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
[]
));
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'token_type' => 'Bearer',
]);
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
}
public function testIssueTokenWithInternalScopes(OauthSteps $I) {
$this->route->issueToken($this->buildParams(
'ely',
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
['account_block']
));
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'invalid_scope',
]);
$this->route->issueToken($this->buildParams(
'trusted-client',
'tXBbyvMcyaOgHMOAXBpN2EC7uFoJAaL9',
['account_block']
));
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'token_type' => 'Bearer',
]);
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
}
private function buildParams($clientId = null, $clientSecret = null, array $scopes = null) {
$params = ['grant_type' => 'client_credentials'];
if ($clientId !== null) {
$params['client_id'] = $clientId;
}
if ($clientSecret !== null) {
$params['client_secret'] = $clientSecret;
}
if ($scopes !== null) {
$params['scope'] = implode(',', $scopes);
}
return $params;
}
}

View File

@@ -0,0 +1,110 @@
<?php
namespace api\tests\functional\oauth;
use api\tests\_pages\OauthRoute;
use api\tests\FunctionalTester;
class CreateClientCest {
/**
* @var OauthRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new OauthRoute($I);
}
public function testCreateApplicationWithWrongParams(FunctionalTester $I) {
$I->amAuthenticated('admin');
$this->route->createClient('application', []);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'name' => 'error.name_required',
'redirectUri' => 'error.redirectUri_required',
],
]);
$this->route->createClient('application', [
'name' => 'my test oauth client',
'redirectUri' => 'localhost',
]);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseContainsJson([
'success' => false,
'errors' => [
'redirectUri' => 'error.redirectUri_invalid',
],
]);
}
public function testCreateApplication(FunctionalTester $I) {
$I->amAuthenticated('admin');
$this->route->createClient('application', [
'name' => 'My admin application',
'description' => 'Application description.',
'redirectUri' => 'http://some-site.com/oauth/ely',
'websiteUrl' => 'http://some-site.com',
]);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
'data' => [
'clientId' => 'my-admin-application',
'name' => 'My admin application',
'description' => 'Application description.',
'websiteUrl' => 'http://some-site.com',
'countUsers' => 0,
'redirectUri' => 'http://some-site.com/oauth/ely',
],
]);
$I->canSeeResponseJsonMatchesJsonPath('$.data.clientSecret');
$I->canSeeResponseJsonMatchesJsonPath('$.data.createdAt');
}
public function testCreateMinecraftServer(FunctionalTester $I) {
$I->amAuthenticated('admin');
$this->route->createClient('minecraft-server', [
'name' => 'My amazing server',
'websiteUrl' => 'http://some-site.com',
'minecraftServerIp' => 'hypixel.com:25565',
]);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
'data' => [
'clientId' => 'my-amazing-server',
'name' => 'My amazing server',
'websiteUrl' => 'http://some-site.com',
'minecraftServerIp' => 'hypixel.com:25565',
],
]);
$I->canSeeResponseJsonMatchesJsonPath('$.data.clientSecret');
$I->canSeeResponseJsonMatchesJsonPath('$.data.createdAt');
}
public function testCreateApplicationWithTheSameNameAsDeletedApp(FunctionalTester $I) {
$I->wantTo('create application with the same name as the recently deleted application');
$I->amAuthenticated('admin');
$this->route->createClient('application', [
'name' => 'Deleted OAuth Client',
'description' => '',
'redirectUri' => 'http://some-site.com/oauth/ely',
'websiteUrl' => 'http://some-site.com',
]);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
'data' => [
'clientId' => 'deleted-oauth-client1',
],
]);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace api\tests\functional\oauth;
use api\tests\_pages\OauthRoute;
use api\tests\FunctionalTester;
class DeleteClientCest {
/**
* @var OauthRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new OauthRoute($I);
}
public function testDelete(FunctionalTester $I) {
$I->amAuthenticated('TwoOauthClients');
$this->route->deleteClient('first-test-oauth-client');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace api\tests\functional\oauth;
use api\tests\_pages\OauthRoute;
use api\tests\FunctionalTester;
class GetClientsCest {
/**
* @var OauthRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new OauthRoute($I);
}
public function testGet(FunctionalTester $I) {
$I->amAuthenticated('admin');
$this->route->getClient('admin-oauth-client');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'clientId' => 'admin-oauth-client',
'clientSecret' => 'FKyO71iCIlv4YM2IHlLbhsvYoIJScUzTZt1kEK7DQLXXYISLDvURVXK32Q58sHWS',
'type' => 'application',
'name' => 'Admin\'s oauth client',
'description' => 'Personal oauth client',
'redirectUri' => 'http://some-site.com/oauth/ely',
'websiteUrl' => '',
'createdAt' => 1519254133,
]);
}
public function testGetNotOwn(FunctionalTester $I) {
$I->amAuthenticated('admin');
$this->route->getClient('another-test-oauth-client');
$I->canSeeResponseCodeIs(403);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'name' => 'Forbidden',
'status' => 403,
'message' => 'You are not allowed to perform this action.',
]);
}
public function testGetAllPerAccountList(FunctionalTester $I) {
$I->amAuthenticated('TwoOauthClients');
$this->route->getPerAccount(14);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
[
'clientId' => 'first-test-oauth-client',
'clientSecret' => 'Zt1kEK7DQLXXYISLDvURVXK32Q58sHWSFKyO71iCIlv4YM2IHlLbhsvYoIJScUzT',
'type' => 'application',
'name' => 'First test oauth client',
'description' => 'Some description to the first oauth client',
'redirectUri' => 'http://some-site-1.com/oauth/ely',
'websiteUrl' => '',
'countUsers' => 0,
'createdAt' => 1519487434,
],
[
'clientId' => 'another-test-oauth-client',
'clientSecret' => 'URVXK32Q58sHWSFKyO71iCIlv4YM2Zt1kEK7DQLXXYISLDvIHlLbhsvYoIJScUzT',
'type' => 'minecraft-server',
'name' => 'Another test oauth client',
'websiteUrl' => '',
'minecraftServerIp' => '136.243.88.97:25565',
'createdAt' => 1519487472,
],
]);
}
public function testGetAllPerNotOwnAccount(FunctionalTester $I) {
$I->amAuthenticated('TwoOauthClients');
$this->route->getPerAccount(1);
$I->canSeeResponseCodeIs(403);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'name' => 'Forbidden',
'status' => 403,
'message' => 'You are not allowed to perform this action.',
]);
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace api\tests\functional\oauth;
use api\tests\_pages\IdentityInfoRoute;
use api\tests\functional\_steps\OauthSteps;
use api\tests\FunctionalTester;
class IdentityInfoCest {
/**
* @var IdentityInfoRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new IdentityInfoRoute($I);
}
public function testGetErrorIfNoAccessToken(OauthSteps $I) {
$I->wantToTest('behavior when this endpoint called without Authorization header');
$this->route->info();
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'name' => 'Unauthorized',
'status' => 401,
'message' => 'Your request was made with invalid credentials.',
]);
}
public function testGetErrorIfNotEnoughPerms(OauthSteps $I) {
$I->wantToTest('behavior when this endpoint called with token, that have not enough scopes');
$accessToken = $I->getAccessToken();
$I->amBearerAuthenticated($accessToken);
$this->route->info();
$I->canSeeResponseCodeIs(403);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'name' => 'Forbidden',
'status' => 403,
'message' => 'You are not allowed to perform this action.',
]);
}
public function testGetInfo(OauthSteps $I) {
$accessToken = $I->getAccessToken(['account_info']);
$I->amBearerAuthenticated($accessToken);
$this->route->info();
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'id' => 1,
'uuid' => 'df936908-b2e1-544d-96f8-2977ec213022',
'username' => 'Admin',
'registeredAt' => 1451775316,
'profileLink' => 'http://ely.by/u1',
'preferredLanguage' => 'en',
]);
$I->cantSeeResponseJsonMatchesJsonPath('$.email');
}
public function testGetInfoWithEmail(OauthSteps $I) {
$accessToken = $I->getAccessToken(['account_info', 'account_email']);
$I->amBearerAuthenticated($accessToken);
$this->route->info();
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'id' => 1,
'uuid' => 'df936908-b2e1-544d-96f8-2977ec213022',
'username' => 'Admin',
'registeredAt' => 1451775316,
'profileLink' => 'http://ely.by/u1',
'preferredLanguage' => 'en',
'email' => 'admin@ely.by',
]);
}
}

View File

@@ -0,0 +1,124 @@
<?php
namespace api\tests\functional\oauth;
use api\components\OAuth2\Storage\ScopeStorage as S;
use common\rbac\Permissions as P;
use api\tests\_pages\OauthRoute;
use api\tests\functional\_steps\OauthSteps;
use api\tests\FunctionalTester;
class RefreshTokenCest {
/**
* @var OauthRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new OauthRoute($I);
}
public function testInvalidRefreshToken(OauthSteps $I) {
$this->route->issueToken($this->buildParams(
'some-invalid-refresh-token',
'ely',
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM'
));
$I->canSeeResponseContainsJson([
'error' => 'invalid_request',
'message' => 'The refresh token is invalid.',
]);
}
public function testRefreshToken(OauthSteps $I) {
$refreshToken = $I->getRefreshToken();
$this->route->issueToken($this->buildParams(
$refreshToken,
'ely',
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM'
));
$this->canSeeRefreshTokenSuccess($I);
}
public function testRefreshTokenWithSameScopes(OauthSteps $I) {
$refreshToken = $I->getRefreshToken([P::MINECRAFT_SERVER_SESSION]);
$this->route->issueToken($this->buildParams(
$refreshToken,
'ely',
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
[P::MINECRAFT_SERVER_SESSION, S::OFFLINE_ACCESS]
));
$this->canSeeRefreshTokenSuccess($I);
}
public function testRefreshTokenTwice(OauthSteps $I) {
$refreshToken = $I->getRefreshToken([P::MINECRAFT_SERVER_SESSION]);
$this->route->issueToken($this->buildParams(
$refreshToken,
'ely',
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
[P::MINECRAFT_SERVER_SESSION, S::OFFLINE_ACCESS]
));
$this->canSeeRefreshTokenSuccess($I);
$this->route->issueToken($this->buildParams(
$refreshToken,
'ely',
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
[P::MINECRAFT_SERVER_SESSION, S::OFFLINE_ACCESS]
));
$this->canSeeRefreshTokenSuccess($I);
}
public function testRefreshTokenWithNewScopes(OauthSteps $I) {
$refreshToken = $I->getRefreshToken([P::MINECRAFT_SERVER_SESSION]);
$this->route->issueToken($this->buildParams(
$refreshToken,
'ely',
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
[P::MINECRAFT_SERVER_SESSION, S::OFFLINE_ACCESS, 'account_email']
));
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'invalid_scope',
]);
}
private function buildParams($refreshToken = null, $clientId = null, $clientSecret = null, $scopes = []) {
$params = ['grant_type' => 'refresh_token'];
if ($refreshToken !== null) {
$params['refresh_token'] = $refreshToken;
}
if ($clientId !== null) {
$params['client_id'] = $clientId;
}
if ($clientSecret !== null) {
$params['client_secret'] = $clientSecret;
}
if (!empty($scopes)) {
if (is_array($scopes)) {
$scopes = implode(',', $scopes);
}
$params['scope'] = $scopes;
}
return $params;
}
private function canSeeRefreshTokenSuccess(OauthSteps $I) {
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'token_type' => 'Bearer',
]);
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
$I->cantSeeResponseJsonMatchesJsonPath('$.refresh_token');
}
}

View File

@@ -0,0 +1,60 @@
<?php
namespace api\tests\functional\oauth;
use api\tests\_pages\OauthRoute;
use api\tests\FunctionalTester;
class ResetClientCest {
/**
* @var OauthRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new OauthRoute($I);
}
public function testReset(FunctionalTester $I) {
$I->amAuthenticated('TwoOauthClients');
$this->route->resetClient('first-test-oauth-client');
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
'data' => [
'clientId' => 'first-test-oauth-client',
'clientSecret' => 'Zt1kEK7DQLXXYISLDvURVXK32Q58sHWSFKyO71iCIlv4YM2IHlLbhsvYoIJScUzT',
'name' => 'First test oauth client',
'description' => 'Some description to the first oauth client',
'redirectUri' => 'http://some-site-1.com/oauth/ely',
'websiteUrl' => '',
'countUsers' => 0,
'createdAt' => 1519487434,
],
]);
}
public function testResetWithSecretChanging(FunctionalTester $I) {
$I->amAuthenticated('TwoOauthClients');
$this->route->resetClient('first-test-oauth-client', true);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
'data' => [
'clientId' => 'first-test-oauth-client',
'name' => 'First test oauth client',
'description' => 'Some description to the first oauth client',
'redirectUri' => 'http://some-site-1.com/oauth/ely',
'websiteUrl' => '',
'countUsers' => 0,
'createdAt' => 1519487434,
],
]);
$I->canSeeResponseJsonMatchesJsonPath('$.data.clientSecret');
$secret = $I->grabDataFromResponseByJsonPath('$.data.clientSecret')[0];
$I->assertNotEquals('Zt1kEK7DQLXXYISLDvURVXK32Q58sHWSFKyO71iCIlv4YM2IHlLbhsvYoIJScUzT', $secret);
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace api\tests\functional\oauth;
use api\tests\_pages\OauthRoute;
use api\tests\FunctionalTester;
class UpdateClientCest {
/**
* @var OauthRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new OauthRoute($I);
}
public function testUpdateApplication(FunctionalTester $I) {
$I->amAuthenticated('TwoOauthClients');
$this->route->updateClient('first-test-oauth-client', [
'name' => 'Updated name',
'description' => 'Updated description.',
'redirectUri' => 'http://new-site.com/oauth/ely',
'websiteUrl' => 'http://new-site.com',
]);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
'data' => [
'clientId' => 'first-test-oauth-client',
'clientSecret' => 'Zt1kEK7DQLXXYISLDvURVXK32Q58sHWSFKyO71iCIlv4YM2IHlLbhsvYoIJScUzT',
'name' => 'Updated name',
'description' => 'Updated description.',
'redirectUri' => 'http://new-site.com/oauth/ely',
'websiteUrl' => 'http://new-site.com',
'createdAt' => 1519487434,
'countUsers' => 0,
],
]);
}
public function testUpdateMinecraftServer(FunctionalTester $I) {
$I->amAuthenticated('TwoOauthClients');
$this->route->updateClient('another-test-oauth-client', [
'name' => 'Updated server name',
'websiteUrl' => 'http://new-site.com',
'minecraftServerIp' => 'hypixel.com:25565',
]);
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
'data' => [
'clientId' => 'another-test-oauth-client',
'clientSecret' => 'URVXK32Q58sHWSFKyO71iCIlv4YM2Zt1kEK7DQLXXYISLDvIHlLbhsvYoIJScUzT',
'name' => 'Updated server name',
'websiteUrl' => 'http://new-site.com',
'minecraftServerIp' => 'hypixel.com:25565',
'createdAt' => 1519487472,
],
]);
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace api\tests\functional\sessionserver;
use Faker\Provider\Uuid;
use api\tests\_pages\SessionServerRoute;
use api\tests\functional\_steps\SessionServerSteps;
use api\tests\FunctionalTester;
class HasJoinedCest {
/**
* @var SessionServerRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new SessionServerRoute($I);
}
public function hasJoined(SessionServerSteps $I) {
$I->wantTo('check hasJoined user to some server');
list($username, $serverId) = $I->amJoined();
$this->route->hasJoined([
'username' => $username,
'serverId' => $serverId,
]);
$I->seeResponseCodeIs(200);
$I->canSeeValidTexturesResponse($username, 'df936908b2e1544d96f82977ec213022');
}
public function wrongArguments(FunctionalTester $I) {
$I->wantTo('get error on wrong amount of arguments');
$this->route->hasJoined([
'wrong' => 'argument',
]);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'IllegalArgumentException',
'errorMessage' => 'credentials can not be null.',
]);
}
public function hasJoinedWithNoJoinOperation(FunctionalTester $I) {
$I->wantTo('hasJoined to some server without join call');
$this->route->hasJoined([
'username' => 'some-username',
'serverId' => Uuid::uuid(),
]);
$I->seeResponseCodeIs(401);
$I->seeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'ForbiddenOperationException',
'errorMessage' => 'Invalid token.',
]);
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace api\tests\functional\sessionserver;
use Faker\Provider\Uuid;
use api\tests\_pages\SessionServerRoute;
use api\tests\functional\_steps\SessionServerSteps;
use api\tests\FunctionalTester;
class HasJoinedLegacyCest {
/**
* @var SessionServerRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new SessionServerRoute($I);
}
public function hasJoined(SessionServerSteps $I) {
$I->wantTo('test hasJoined user to some server by legacy version');
list($username, $serverId) = $I->amJoined(true);
$this->route->hasJoinedLegacy([
'user' => $username,
'serverId' => $serverId,
]);
$I->seeResponseCodeIs(200);
$I->canSeeResponseEquals('YES');
}
public function wrongArguments(FunctionalTester $I) {
$I->wantTo('get error on wrong amount of arguments');
$this->route->hasJoinedLegacy([
'wrong' => 'argument',
]);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseEquals('credentials can not be null.');
}
public function hasJoinedWithNoJoinOperation(FunctionalTester $I) {
$I->wantTo('hasJoined by legacy version to some server without join call');
$this->route->hasJoinedLegacy([
'user' => 'random-username',
'serverId' => Uuid::uuid(),
]);
$I->seeResponseCodeIs(200);
$I->canSeeResponseEquals('NO');
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace api\tests\functional\sessionserver;
use common\rbac\Permissions as P;
use Faker\Provider\Uuid;
use api\tests\_pages\SessionServerRoute;
use api\tests\functional\_steps\AuthserverSteps;
use api\tests\functional\_steps\OauthSteps;
use api\tests\FunctionalTester;
class JoinCest {
/**
* @var SessionServerRoute
*/
private $route;
public function _before(AuthserverSteps $I) {
$this->route = new SessionServerRoute($I);
}
public function joinByLegacyAuthserver(AuthserverSteps $I) {
$I->wantTo('join to server, using legacy authserver access token');
[$accessToken] = $I->amAuthenticated();
$this->route->join([
'accessToken' => $accessToken,
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
'serverId' => Uuid::uuid(),
]);
$this->expectSuccessResponse($I);
}
public function joinByPassJsonInPost(AuthserverSteps $I) {
$I->wantTo('join to server, passing data in body as encoded json');
[$accessToken] = $I->amAuthenticated();
$this->route->join(json_encode([
'accessToken' => $accessToken,
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
'serverId' => Uuid::uuid(),
]));
$this->expectSuccessResponse($I);
}
public function joinByOauth2Token(OauthSteps $I) {
$I->wantTo('join to server, using modern oAuth2 generated token');
$accessToken = $I->getAccessToken([P::MINECRAFT_SERVER_SESSION]);
$this->route->join([
'accessToken' => $accessToken,
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
'serverId' => Uuid::uuid(),
]);
$this->expectSuccessResponse($I);
}
public function joinByOauth2TokenWithNotDashedUUID(OauthSteps $I) {
$I->wantTo('join to server, using modern oAuth2 generated token and non dashed uuid');
$accessToken = $I->getAccessToken([P::MINECRAFT_SERVER_SESSION]);
$this->route->join([
'accessToken' => $accessToken,
'selectedProfile' => 'df936908b2e1544d96f82977ec213022',
'serverId' => Uuid::uuid(),
]);
$this->expectSuccessResponse($I);
}
public function joinByModernOauth2TokenWithoutPermission(OauthSteps $I) {
$I->wantTo('join to server, using moder oAuth2 generated token, but without minecraft auth permission');
$accessToken = $I->getAccessToken(['account_info', 'account_email']);
$this->route->join([
'accessToken' => $accessToken,
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
'serverId' => Uuid::uuid(),
]);
$I->seeResponseCodeIs(401);
$I->seeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'ForbiddenOperationException',
'errorMessage' => 'The token does not have required scope.',
]);
}
public function joinWithExpiredToken(FunctionalTester $I) {
$I->wantTo('join to some server with expired accessToken');
$this->route->join([
'accessToken' => '6042634a-a1e2-4aed-866c-c661fe4e63e2',
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
'serverId' => Uuid::uuid(),
]);
$I->seeResponseCodeIs(401);
$I->seeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'ForbiddenOperationException',
'errorMessage' => 'Expired access_token.',
]);
}
public function wrongArguments(FunctionalTester $I) {
$I->wantTo('get error on wrong amount of arguments');
$this->route->join([
'wrong' => 'argument',
]);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'IllegalArgumentException',
'errorMessage' => 'credentials can not be null.',
]);
}
public function joinWithWrongAccessToken(FunctionalTester $I) {
$I->wantTo('join to some server with wrong accessToken');
$this->route->join([
'accessToken' => Uuid::uuid(),
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
'serverId' => Uuid::uuid(),
]);
$I->seeResponseCodeIs(401);
$I->seeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'ForbiddenOperationException',
'errorMessage' => 'Invalid access_token.',
]);
}
public function joinWithNilUuids(FunctionalTester $I) {
$I->wantTo('join to some server with nil accessToken and selectedProfile');
$this->route->join([
'accessToken' => '00000000-0000-0000-0000-000000000000',
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
'serverId' => Uuid::uuid(),
]);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'IllegalArgumentException',
'errorMessage' => 'credentials can not be null.',
]);
}
private function expectSuccessResponse(FunctionalTester $I) {
$I->seeResponseCodeIs(200);
$I->seeResponseIsJson();
$I->canSeeResponseContainsJson([
'id' => 'OK',
]);
}
}

View File

@@ -0,0 +1,114 @@
<?php
namespace api\tests\functional\sessionserver;
use common\rbac\Permissions as P;
use Faker\Provider\Uuid;
use api\tests\_pages\SessionServerRoute;
use api\tests\functional\_steps\AuthserverSteps;
use api\tests\functional\_steps\OauthSteps;
use api\tests\FunctionalTester;
class JoinLegacyCest {
/**
* @var SessionServerRoute
*/
private $route;
public function _before(AuthserverSteps $I) {
$this->route = new SessionServerRoute($I);
}
public function joinByLegacyAuthserver(AuthserverSteps $I) {
$I->wantTo('join to server by legacy protocol, using legacy authserver access token');
[$accessToken] = $I->amAuthenticated();
$this->route->joinLegacy([
'sessionId' => $accessToken,
'user' => 'Admin',
'serverId' => Uuid::uuid(),
]);
$this->expectSuccessResponse($I);
}
public function joinByOauth2TokenAndDifferentLetterCase(AuthserverSteps $I) {
$I->wantTo('join to server by legacy protocol, using legacy authserver access token and different letter case');
[$accessToken] = $I->amAuthenticated();
$this->route->joinLegacy([
'sessionId' => $accessToken,
'user' => 'admin',
'serverId' => Uuid::uuid(),
]);
$this->expectSuccessResponse($I);
}
public function joinByNewSessionFormat(AuthserverSteps $I) {
$I->wantTo('join to server by legacy protocol with new launcher session format, using legacy authserver');
[$accessToken] = $I->amAuthenticated();
$this->route->joinLegacy([
'sessionId' => 'token:' . $accessToken . ':' . 'df936908-b2e1-544d-96f8-2977ec213022',
'user' => 'Admin',
'serverId' => Uuid::uuid(),
]);
$this->expectSuccessResponse($I);
}
public function joinByOauth2Token(OauthSteps $I) {
$I->wantTo('join to server using modern oAuth2 generated token with new launcher session format');
$accessToken = $I->getAccessToken([P::MINECRAFT_SERVER_SESSION]);
$this->route->joinLegacy([
'sessionId' => 'token:' . $accessToken . ':' . 'df936908-b2e1-544d-96f8-2977ec213022',
'user' => 'Admin',
'serverId' => Uuid::uuid(),
]);
$this->expectSuccessResponse($I);
}
public function wrongArguments(FunctionalTester $I) {
$I->wantTo('get error on wrong amount of arguments');
$this->route->joinLegacy([
'wrong' => 'argument',
]);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseContains('credentials can not be null.');
}
public function joinWithWrongAccessToken(FunctionalTester $I) {
$I->wantTo('join to some server with wrong accessToken');
$this->route->joinLegacy([
'sessionId' => 'token:' . Uuid::uuid() . ':' . Uuid::uuid(),
'user' => 'random-username',
'serverId' => Uuid::uuid(),
]);
$I->seeResponseCodeIs(401);
$I->canSeeResponseContains('Ely.by authorization required');
}
public function joinWithAccessTokenWithoutMinecraftPermission(OauthSteps $I) {
$I->wantTo('join to some server with wrong accessToken');
$accessToken = $I->getAccessToken(['account_info']);
$this->route->joinLegacy([
'sessionId' => 'token:' . $accessToken . ':' . 'df936908-b2e1-544d-96f8-2977ec213022',
'user' => 'Admin',
'serverId' => Uuid::uuid(),
]);
$I->seeResponseCodeIs(401);
$I->canSeeResponseContains('Ely.by authorization required');
}
public function joinWithNilUuids(FunctionalTester $I) {
$I->wantTo('join to some server by legacy protocol with nil accessToken and selectedProfile');
$this->route->joinLegacy([
'sessionId' => 'token:00000000-0000-0000-0000-000000000000:00000000-0000-0000-0000-000000000000',
'user' => 'SomeUser',
'serverId' => Uuid::uuid(),
]);
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseContains('credentials can not be null.');
}
private function expectSuccessResponse(FunctionalTester $I) {
$I->seeResponseCodeIs(200);
$I->canSeeResponseEquals('OK');
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace api\tests\functional\sessionserver;
use Faker\Provider\Uuid;
use api\tests\_pages\SessionServerRoute;
use api\tests\functional\_steps\SessionServerSteps;
use api\tests\FunctionalTester;
class ProfileCest {
/**
* @var SessionServerRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new SessionServerRoute($I);
}
public function getProfile(SessionServerSteps $I) {
$I->wantTo('get info about player textures by uuid');
$this->route->profile('df936908-b2e1-544d-96f8-2977ec213022');
$I->canSeeValidTexturesResponse('Admin', 'df936908b2e1544d96f82977ec213022');
}
public function getProfileByUuidWithoutDashes(SessionServerSteps $I) {
$I->wantTo('get info about player textures by uuid without dashes');
$this->route->profile('df936908b2e1544d96f82977ec213022');
$I->canSeeValidTexturesResponse('Admin', 'df936908b2e1544d96f82977ec213022');
}
public function directCallWithoutUuidPart(FunctionalTester $I) {
$I->wantTo('call profile route without passing uuid');
$this->route->profile('');
$I->canSeeResponseCodeIs(404);
}
public function callWithInvalidUuid(FunctionalTester $I) {
$I->wantTo('call profile route with invalid uuid string');
$this->route->profile('bla-bla-bla');
$I->canSeeResponseCodeIs(400);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'IllegalArgumentException',
'errorMessage' => 'Invalid uuid format.',
]);
}
public function getProfileWithNonexistentUuid(FunctionalTester $I) {
$I->wantTo('get info about nonexistent uuid');
$this->route->profile(Uuid::uuid());
$I->canSeeResponseCodeIs(401);
$I->canSeeResponseIsJson();
$I->seeResponseIsJson();
$I->canSeeResponseContainsJson([
'error' => 'ForbiddenOperationException',
'errorMessage' => 'Invalid uuid.',
]);
}
}