From 4ee8544355e19470667a73ac6739697a7949c2f8 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sun, 25 Mar 2018 22:21:22 +0300 Subject: [PATCH 01/19] Fix 403 error on not exists application. Remove countUsers from minecraft server application type. --- api/modules/oauth/controllers/ClientsController.php | 5 +++-- common/rbac/rules/OauthClientOwner.php | 2 +- .../common/unit/rbac/rules/OauthClientOwnerTest.php | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/api/modules/oauth/controllers/ClientsController.php b/api/modules/oauth/controllers/ClientsController.php index 3e7960a..485c1c5 100644 --- a/api/modules/oauth/controllers/ClientsController.php +++ b/api/modules/oauth/controllers/ClientsController.php @@ -136,7 +136,8 @@ class ClientsController extends Controller { throw new NotFoundHttpException(); } - $clients = $account->oauthClients; + /** @var OauthClient[] $clients */ + $clients = $account->getOauthClients()->orderBy(['created_at' => SORT_ASC])->all(); $result = array_map(function(OauthClient $client) { return $this->formatClient($client); }, $clients); @@ -152,13 +153,13 @@ class ClientsController extends Controller { 'name' => $client->name, 'websiteUrl' => $client->website_url, 'createdAt' => $client->created_at, - 'countUsers' => (int)$client->getSessions()->count(), ]; switch ($client->type) { case OauthClient::TYPE_APPLICATION: $result['description'] = $client->description; $result['redirectUri'] = $client->redirect_uri; + $result['countUsers'] = (int)$client->getSessions()->count(); break; case OauthClient::TYPE_MINECRAFT_SERVER: $result['minecraftServerIp'] = $client->minecraft_server_ip; diff --git a/common/rbac/rules/OauthClientOwner.php b/common/rbac/rules/OauthClientOwner.php index e303743..b03636a 100644 --- a/common/rbac/rules/OauthClientOwner.php +++ b/common/rbac/rules/OauthClientOwner.php @@ -38,7 +38,7 @@ class OauthClientOwner extends Rule { /** @var OauthClient|null $client */ $client = OauthClient::findOne($clientId); if ($client === null) { - return false; + return true; } $identity = Yii::$app->user->findIdentityByAccessToken($accessToken); diff --git a/tests/codeception/common/unit/rbac/rules/OauthClientOwnerTest.php b/tests/codeception/common/unit/rbac/rules/OauthClientOwnerTest.php index 1919377..643ab79 100644 --- a/tests/codeception/common/unit/rbac/rules/OauthClientOwnerTest.php +++ b/tests/codeception/common/unit/rbac/rules/OauthClientOwnerTest.php @@ -42,7 +42,7 @@ class OauthClientOwnerTest extends TestCase { $this->assertFalse($rule->execute('token', $item, [])); $this->assertTrue($rule->execute('token', $item, ['clientId' => 'admin-oauth-client'])); - $this->assertFalse($rule->execute('token', $item, ['clientId' => 'not-exists-client'])); + $this->assertTrue($rule->execute('token', $item, ['clientId' => 'not-exists-client'])); $account->id = 2; $this->assertFalse($rule->execute('token', $item, ['clientId' => 'admin-oauth-client'])); $item->name = P::VIEW_OWN_OAUTH_CLIENTS; From bfdcaf2233ce3f136cef475c1d56077cb008c2c4 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sun, 25 Mar 2018 22:59:34 +0300 Subject: [PATCH 02/19] Fix tests for applications managing API --- tests/codeception/api/functional/oauth/CreateClientCest.php | 1 - tests/codeception/api/functional/oauth/GetClientsCest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/codeception/api/functional/oauth/CreateClientCest.php b/tests/codeception/api/functional/oauth/CreateClientCest.php index 4f6cc99..665367f 100644 --- a/tests/codeception/api/functional/oauth/CreateClientCest.php +++ b/tests/codeception/api/functional/oauth/CreateClientCest.php @@ -81,7 +81,6 @@ class CreateClientCest { 'clientId' => 'my-amazing-server', 'name' => 'My amazing server', 'websiteUrl' => 'http://some-site.com', - 'countUsers' => 0, 'minecraftServerIp' => 'hypixel.com:25565', ], ]); diff --git a/tests/codeception/api/functional/oauth/GetClientsCest.php b/tests/codeception/api/functional/oauth/GetClientsCest.php index 300d969..6e22dc6 100644 --- a/tests/codeception/api/functional/oauth/GetClientsCest.php +++ b/tests/codeception/api/functional/oauth/GetClientsCest.php @@ -68,7 +68,6 @@ class GetClientsCest { 'name' => 'Another test oauth client', 'websiteUrl' => '', 'minecraftServerIp' => '136.243.88.97:25565', - 'countUsers' => 0, 'createdAt' => 1519487472, ], ]); From 02ea7346a866ac89fef2cfaf00585833e9f7a564 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Tue, 17 Apr 2018 23:47:25 +0300 Subject: [PATCH 03/19] Implemented PHP-CS-Fixer support --- .gitignore | 4 + .gitlab-ci.yml | 13 + .php_cs.dist | 16 + api/aop/AspectKernel.php | 1 - api/components/OAuth2/Component.php | 1 - .../OAuth2/Entities/RefreshTokenEntity.php | 10 +- .../OAuth2/Grants/AuthCodeGrant.php | 6 +- .../OAuth2/Storage/AccessTokenStorage.php | 6 +- .../OAuth2/Storage/AuthCodeStorage.php | 4 +- .../OAuth2/Storage/ClientStorage.php | 6 +- .../OAuth2/Storage/RefreshTokenStorage.php | 4 +- .../OAuth2/Storage/SessionStorage.php | 2 +- api/components/User/Component.php | 2 +- api/components/User/Identity.php | 8 +- api/components/User/JwtIdentity.php | 10 +- api/components/User/ScopesClaim.php | 2 +- api/components/User/SubjectPrefixVerifier.php | 2 +- api/controllers/SignupController.php | 2 +- api/models/FeedbackForm.php | 4 +- .../authentication/ForgotPasswordForm.php | 2 +- api/models/authentication/LoginForm.php | 5 +- api/models/authentication/LogoutForm.php | 2 +- .../authentication/RegistrationForm.php | 4 +- .../RepeatAccountActivationForm.php | 2 +- .../accounts/actions/BaseAccountAction.php | 4 +- .../accounts/actions/ChangeEmailAction.php | 8 +- .../actions/EmailVerificationAction.php | 8 +- .../accounts/models/ChangeEmailForm.php | 2 +- api/modules/accounts/models/TotpHelper.php | 4 +- .../controllers/AuthenticationController.php | 8 +- .../authserver/models/AuthenticateData.php | 4 +- .../authserver/models/AuthenticationForm.php | 14 +- .../authserver/models/InvalidateForm.php | 3 +- .../authserver/models/RefreshTokenForm.php | 1 + api/modules/authserver/models/SignoutForm.php | 3 +- .../authserver/models/ValidateForm.php | 2 +- .../mojang/controllers/ApiController.php | 8 +- .../controllers/AuthorizationController.php | 2 +- .../oauth/exceptions/OauthException.php | 1 + api/modules/oauth/models/ApplicationType.php | 2 +- .../session/controllers/SessionController.php | 2 +- .../ForbiddenOperationException.php | 2 +- api/modules/session/filters/RateLimiter.php | 3 +- api/modules/session/models/JoinForm.php | 4 +- api/modules/session/models/SessionModel.php | 2 +- .../models/protocols/BaseHasJoined.php | 1 + .../session/models/protocols/LegacyJoin.php | 5 +- .../session/models/protocols/ModernJoin.php | 2 + api/traits/AccountFinder.php | 2 +- common/behaviors/DataBehavior.php | 1 + .../EmailActivationExpirationBehavior.php | 12 +- common/behaviors/PrimaryKeyValueBehavior.php | 8 +- common/components/EmailRenderer.php | 2 +- common/components/Qr/ElyDecorator.php | 1 - common/components/RabbitMQ/Component.php | 80 +-- common/components/Redis/Connection.php | 18 +- common/components/Redis/Key.php | 6 +- common/components/Redis/Set.php | 2 + common/components/SkinSystem/Api.php | 2 +- common/components/UserFriendlyRandomKey.php | 5 +- common/components/UserPass.php | 1 - common/config/ConfigLoader.php | 6 +- common/config/config.php | 2 +- common/db/mysql/QueryBuilder.php | 11 +- common/emails/TemplateWithRenderer.php | 2 +- .../ChangeEmailConfirmCurrentEmail.php | 12 +- .../templates/ChangeEmailConfirmNewEmail.php | 14 +- common/helpers/Error.php | 92 ++-- common/helpers/StringHelper.php | 4 +- common/models/Account.php | 17 +- common/models/EmailActivation.php | 16 +- common/models/MinecraftAccessKey.php | 2 +- common/models/OauthScopeQuery.php | 8 +- common/models/Textures.php | 6 +- common/models/UsernameHistory.php | 2 +- .../confirmations/NewEmailConfirmation.php | 2 +- .../NewEmailConfirmationBehavior.php | 4 +- composer.json | 4 +- composer.lock | 465 +++++++++++++++++- console/controllers/AmqpController.php | 2 +- console/controllers/CleanupController.php | 2 +- console/controllers/RbacController.php | 2 +- console/db/Migration.php | 2 +- console/migrations/m160201_055928_oauth.php | 26 +- ...60414_231110_account_nicknames_history.php | 2 +- ...m160819_211139_minecraft_access_tokens.php | 6 +- .../m161030_013122_ely_by_admin_app.php | 8 +- ...1222_222520_remove_oauth_access_tokens.php | 4 +- tests/codeception/api/_bootstrap.php | 4 +- tests/codeception/api/_support/UnitTester.php | 10 +- .../functional/_steps/SessionServerSteps.php | 2 +- .../authserver/AuthorizationCest.php | 2 +- .../mojang/UsernamesToUuidsCest.php | 3 +- tests/codeception/api/unit/TestCase.php | 12 +- .../unit/components/User/ComponentTest.php | 2 +- .../models/authentication/LoginFormTest.php | 14 +- .../models/authentication/LogoutFormTest.php | 4 +- .../modules/internal/models/BanFormTest.php | 2 +- .../internal/models/PardonFormTest.php | 2 +- .../oauth/models/OauthClientFormTest.php | 2 - .../session/filters/RateLimiterTest.php | 4 +- .../PasswordRequiredValidatorTest.php | 2 +- .../common/_support/ProtectedCaller.php | 1 - .../common/_support/UnitTester.php | 7 +- .../common/_support/amqp/Helper.php | 2 +- .../common/_support/amqp/TestComponent.php | 4 +- .../common/_support/queue/Queue.php | 10 +- tests/codeception/common/unit/TestCase.php | 12 +- .../behaviors/PrimaryKeyValueBehaviorTest.php | 26 +- .../unit/validators/EmailValidatorTest.php | 2 +- .../unit/validators/UsernameValidatorTest.php | 9 +- .../console/_support/UnitTester.php | 10 +- tests/codeception/console/unit/TestCase.php | 10 +- .../AccountQueueControllerTest.php | 4 +- yii | 8 +- 115 files changed, 883 insertions(+), 363 deletions(-) create mode 100644 .php_cs.dist diff --git a/.gitignore b/.gitignore index 7749dee..f18c018 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,7 @@ npm-debug* # id_rsa /id_rsa + +# PHP-CS-Fixer +.php_cs +.php_cs.cache diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a5d6184..d24b075 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,6 +24,19 @@ test:backend: script: - export TEMP_DEV_IMAGE="${CONTAINER_IMAGE}:ci-${CI_BUILD_ID}" - docker build --pull -f Dockerfile-dev -t $TEMP_DEV_IMAGE . + # https://github.com/FriendsOfPHP/PHP-CS-Fixer#using-php-cs-fixer-on-ci + - COMMIT_RANGE="${CI_COMMIT_BEFORE_SHA}...${CI_COMMIT_SHA}" + - CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRTUXB "${COMMIT_RANGE}") + - | + if ! echo "${CHANGED_FILES}" | grep -qE "^(\\.php_cs(\\.dist)?|composer\\.lock)$"; then + EXTRA_ARGS=$(printf -- '--path-mode=intersection\n--\n%s' "${CHANGED_FILES}") + else + EXTRA_ARGS='' + fi + - > + docker run --rm + $TEMP_DEV_IMAGE + vendor/bin/php-cs-fixer fix -v --dry-run --stop-on-violation --using-cache=no ${EXTRA_ARGS} - > docker run --rm --add-host=mariadb:`getent hosts mariadb | awk '{ print $1 ; exit }'` diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 0000000..a8fc27d --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,16 @@ +in(__DIR__) + ->exclude('data') + ->exclude('docker') + ->exclude('frontend') + ->notPath('common/emails/views') + ->notPath('/.*\/runtime/') + ->notPath('autocompletion.php') + ->notPath('tests/codeception/_output') + ->notPath('/tests\/codeception\/.*\/_output/') + ->notPath('/tests\/codeception\/.*\/_support\/_generated/') + ->name('yii'); + +return \Ely\CS\Config::create() + ->setFinder($finder); diff --git a/api/aop/AspectKernel.php b/api/aop/AspectKernel.php index bd03dc3..5cfb386 100644 --- a/api/aop/AspectKernel.php +++ b/api/aop/AspectKernel.php @@ -1,7 +1,6 @@ session instanceof SessionEntity) { return $this->session; } @@ -26,18 +26,18 @@ class RefreshTokenEntity extends \League\OAuth2\Server\Entity\RefreshTokenEntity return $sessionStorage->getById($this->sessionId); } - public function getSessionId() : int { + public function getSessionId(): int { return $this->sessionId; } - public function setSession(OriginalSessionEntity $session) { + public function setSession(OriginalSessionEntity $session): self { parent::setSession($session); $this->setSessionId($session->getId()); return $this; } - public function setSessionId(int $sessionId) { + public function setSessionId(int $sessionId): void { $this->sessionId = $sessionId; } diff --git a/api/components/OAuth2/Grants/AuthCodeGrant.php b/api/components/OAuth2/Grants/AuthCodeGrant.php index c1f5dca..7b7279b 100644 --- a/api/components/OAuth2/Grants/AuthCodeGrant.php +++ b/api/components/OAuth2/Grants/AuthCodeGrant.php @@ -133,8 +133,10 @@ class AuthCodeGrant extends AbstractGrant { throw new Exception\InvalidRequestException('client_id'); } - $clientSecret = $this->server->getRequest()->request->get('client_secret', - $this->server->getRequest()->getPassword()); + $clientSecret = $this->server->getRequest()->request->get( + 'client_secret', + $this->server->getRequest()->getPassword() + ); if ($clientSecret === null && $this->shouldRequireClientSecret()) { throw new Exception\InvalidRequestException('client_secret'); } diff --git a/api/components/OAuth2/Storage/AccessTokenStorage.php b/api/components/OAuth2/Storage/AccessTokenStorage.php index c31742b..2d54cbf 100644 --- a/api/components/OAuth2/Storage/AccessTokenStorage.php +++ b/api/components/OAuth2/Storage/AccessTokenStorage.php @@ -31,7 +31,7 @@ class AccessTokenStorage extends AbstractStorage implements AccessTokenInterface public function getScopes(OriginalAccessTokenEntity $token) { $scopes = $this->scopes($token->getId()); $entities = []; - foreach($scopes as $scope) { + foreach ($scopes as $scope) { if ($this->server->getScopeStorage()->get($scope) !== null) { $entities[] = (new ScopeEntity($this->server))->hydrate(['id' => $scope]); } @@ -59,11 +59,11 @@ class AccessTokenStorage extends AbstractStorage implements AccessTokenInterface $this->scopes($token->getId())->delete(); } - private function key(string $token) : Key { + private function key(string $token): Key { return new Key($this->dataTable, $token); } - private function scopes(string $token) : Set { + private function scopes(string $token): Set { return new Set($this->dataTable, $token, 'scopes'); } diff --git a/api/components/OAuth2/Storage/AuthCodeStorage.php b/api/components/OAuth2/Storage/AuthCodeStorage.php index 77d7f51..681ec57 100644 --- a/api/components/OAuth2/Storage/AuthCodeStorage.php +++ b/api/components/OAuth2/Storage/AuthCodeStorage.php @@ -61,11 +61,11 @@ class AuthCodeStorage extends AbstractStorage implements AuthCodeInterface { $this->scopes($token->getId())->delete(); } - private function key(string $token) : Key { + private function key(string $token): Key { return new Key($this->dataTable, $token); } - private function scopes(string $token) : Set { + private function scopes(string $token): Set { return new Set($this->dataTable, $token, 'scopes'); } diff --git a/api/components/OAuth2/Storage/ClientStorage.php b/api/components/OAuth2/Storage/ClientStorage.php index d5979e2..d7cca10 100644 --- a/api/components/OAuth2/Storage/ClientStorage.php +++ b/api/components/OAuth2/Storage/ClientStorage.php @@ -11,8 +11,8 @@ use yii\helpers\StringHelper; class ClientStorage extends AbstractStorage implements ClientInterface { - const REDIRECT_STATIC_PAGE = 'static_page'; - const REDIRECT_STATIC_PAGE_WITH_CODE = 'static_page_with_code'; + private const REDIRECT_STATIC_PAGE = 'static_page'; + private const REDIRECT_STATIC_PAGE_WITH_CODE = 'static_page_with_code'; /** * @inheritdoc @@ -66,7 +66,7 @@ class ClientStorage extends AbstractStorage implements ClientInterface { return $this->hydrate($model); } - private function hydrate(OauthClient $model) : ClientEntity { + private function hydrate(OauthClient $model): ClientEntity { $entity = new ClientEntity($this->server); $entity->setId($model->id); $entity->setName($model->name); diff --git a/api/components/OAuth2/Storage/RefreshTokenStorage.php b/api/components/OAuth2/Storage/RefreshTokenStorage.php index c1acfe6..6764b2d 100644 --- a/api/components/OAuth2/Storage/RefreshTokenStorage.php +++ b/api/components/OAuth2/Storage/RefreshTokenStorage.php @@ -51,12 +51,12 @@ class RefreshTokenStorage extends AbstractStorage implements RefreshTokenInterfa $this->sessionHash($token->getSessionId())->remove($token->getId()); } - public function sessionHash(string $sessionId) : Set { + public function sessionHash(string $sessionId): Set { $tableName = Yii::$app->db->getSchema()->getRawTableName(OauthSession::tableName()); return new Set($tableName, $sessionId, 'refresh_tokens'); } - private function key(string $token) : Key { + private function key(string $token): Key { return new Key($this->dataTable, $token); } diff --git a/api/components/OAuth2/Storage/SessionStorage.php b/api/components/OAuth2/Storage/SessionStorage.php index c056f9b..2efdfe4 100644 --- a/api/components/OAuth2/Storage/SessionStorage.php +++ b/api/components/OAuth2/Storage/SessionStorage.php @@ -76,7 +76,7 @@ class SessionStorage extends AbstractStorage implements SessionInterface { $this->getSessionModel($session->getId())->getScopes()->add($scope->getId()); } - private function getSessionModel(string $sessionId) : OauthSession { + private function getSessionModel(string $sessionId): OauthSession { $session = OauthSession::findOne($sessionId); if ($session === null) { throw new ErrorException('Cannot find oauth session'); diff --git a/api/components/User/Component.php b/api/components/User/Component.php index 95b52fd..97ec500 100644 --- a/api/components/User/Component.php +++ b/api/components/User/Component.php @@ -214,7 +214,7 @@ class Component extends YiiUserComponent { protected function createToken(Account $account): Token { $token = new Token(); - foreach($this->getClaims($account) as $claim) { + foreach ($this->getClaims($account) as $claim) { $token->addClaim($claim); } diff --git a/api/components/User/Identity.php b/api/components/User/Identity.php index 89a2286..0220eb2 100644 --- a/api/components/User/Identity.php +++ b/api/components/User/Identity.php @@ -18,6 +18,10 @@ class Identity implements IdentityInterface { */ private $_accessToken; + private function __construct(AccessTokenEntity $accessToken) { + $this->_accessToken = $accessToken; + } + /** * @inheritdoc * @throws \yii\web\UnauthorizedHttpException @@ -73,10 +77,6 @@ class Identity implements IdentityInterface { throw new NotSupportedException('This method used for cookie auth, except we using Bearer auth'); } - private function __construct(AccessTokenEntity $accessToken) { - $this->_accessToken = $accessToken; - } - private function getSession(): OauthSession { return OauthSession::findOne($this->_accessToken->getSessionId()); } diff --git a/api/components/User/JwtIdentity.php b/api/components/User/JwtIdentity.php index 78a8491..71b41e1 100644 --- a/api/components/User/JwtIdentity.php +++ b/api/components/User/JwtIdentity.php @@ -23,6 +23,11 @@ class JwtIdentity implements IdentityInterface { */ private $token; + private function __construct(string $rawToken, Token $token) { + $this->rawToken = $rawToken; + $this->token = $token; + } + public static function findIdentityByAccessToken($rawToken, $type = null): IdentityInterface { /** @var \api\components\User\Component $component */ $component = Yii::$app->user; @@ -86,9 +91,4 @@ class JwtIdentity implements IdentityInterface { throw new NotSupportedException('This method used for cookie auth, except we using Bearer auth'); } - private function __construct(string $rawToken, Token $token) { - $this->rawToken = $rawToken; - $this->token = $token; - } - } diff --git a/api/components/User/ScopesClaim.php b/api/components/User/ScopesClaim.php index 743c87f..d89c76b 100644 --- a/api/components/User/ScopesClaim.php +++ b/api/components/User/ScopesClaim.php @@ -5,7 +5,7 @@ use Emarref\Jwt\Claim\AbstractClaim; class ScopesClaim extends AbstractClaim { - const NAME = 'ely-scopes'; + public const NAME = 'ely-scopes'; /** * ScopesClaim constructor. diff --git a/api/components/User/SubjectPrefixVerifier.php b/api/components/User/SubjectPrefixVerifier.php index e784dbd..614d6d7 100644 --- a/api/components/User/SubjectPrefixVerifier.php +++ b/api/components/User/SubjectPrefixVerifier.php @@ -21,7 +21,7 @@ class SubjectPrefixVerifier implements VerifierInterface { $subject = ($subjectClaim === null) ? null : $subjectClaim->getValue(); if (!StringHelper::startsWith($subject, $this->subjectPrefix)) { - throw new InvalidSubjectException; + throw new InvalidSubjectException(); } } diff --git a/api/controllers/SignupController.php b/api/controllers/SignupController.php index 04e1f86..3634dc7 100644 --- a/api/controllers/SignupController.php +++ b/api/controllers/SignupController.php @@ -2,8 +2,8 @@ namespace api\controllers; use api\models\authentication\ConfirmEmailForm; -use api\models\authentication\RepeatAccountActivationForm; use api\models\authentication\RegistrationForm; +use api\models\authentication\RepeatAccountActivationForm; use common\helpers\Error as E; use Yii; use yii\filters\AccessControl; diff --git a/api/models/FeedbackForm.php b/api/models/FeedbackForm.php index 52d899f..6e5730c 100644 --- a/api/models/FeedbackForm.php +++ b/api/models/FeedbackForm.php @@ -1,8 +1,8 @@ validate()) { return false; } diff --git a/api/models/authentication/ForgotPasswordForm.php b/api/models/authentication/ForgotPasswordForm.php index 45cf5cc..b0e5bc2 100644 --- a/api/models/authentication/ForgotPasswordForm.php +++ b/api/models/authentication/ForgotPasswordForm.php @@ -4,9 +4,9 @@ namespace api\models\authentication; use api\aop\annotations\CollectModelMetrics; use api\components\ReCaptcha\Validator as ReCaptchaValidator; use api\models\base\ApiForm; -use common\helpers\Error as E; use api\traits\AccountFinder; use common\components\UserFriendlyRandomKey; +use common\helpers\Error as E; use common\models\Account; use common\models\confirmations\ForgotPassword; use common\models\EmailActivation; diff --git a/api/models/authentication/LoginForm.php b/api/models/authentication/LoginForm.php index c5a2948..8919300 100644 --- a/api/models/authentication/LoginForm.php +++ b/api/models/authentication/LoginForm.php @@ -3,9 +3,9 @@ namespace api\models\authentication; use api\aop\annotations\CollectModelMetrics; use api\models\base\ApiForm; +use api\traits\AccountFinder; use api\validators\TotpValidator; use common\helpers\Error as E; -use api\traits\AccountFinder; use common\models\Account; use Yii; @@ -13,8 +13,11 @@ class LoginForm extends ApiForm { use AccountFinder; public $login; + public $password; + public $totp; + public $rememberMe = false; public function rules(): array { diff --git a/api/models/authentication/LogoutForm.php b/api/models/authentication/LogoutForm.php index 8028c68..6f05ac0 100644 --- a/api/models/authentication/LogoutForm.php +++ b/api/models/authentication/LogoutForm.php @@ -11,7 +11,7 @@ class LogoutForm extends ApiForm { * @CollectModelMetrics(prefix="authentication.logout") * @return bool */ - public function logout() : bool { + public function logout(): bool { $component = Yii::$app->user; $session = $component->getActiveSession(); if ($session === null) { diff --git a/api/models/authentication/RegistrationForm.php b/api/models/authentication/RegistrationForm.php index 2382fd5..fba45d4 100644 --- a/api/models/authentication/RegistrationForm.php +++ b/api/models/authentication/RegistrationForm.php @@ -4,8 +4,8 @@ namespace api\models\authentication; use api\aop\annotations\CollectModelMetrics; use api\components\ReCaptcha\Validator as ReCaptchaValidator; use api\models\base\ApiForm; -use common\helpers\Error as E; use common\components\UserFriendlyRandomKey; +use common\helpers\Error as E; use common\models\Account; use common\models\confirmations\RegistrationConfirmation; use common\models\UsernameHistory; @@ -126,7 +126,7 @@ class RegistrationForm extends ApiForm { * * @return bool */ - protected function canContinue(array $errors) : bool { + protected function canContinue(array $errors): bool { if (ArrayHelper::getValue($errors, 'username') === E::USERNAME_NOT_AVAILABLE) { $duplicatedUsername = Account::findOne([ 'username' => $this->username, diff --git a/api/models/authentication/RepeatAccountActivationForm.php b/api/models/authentication/RepeatAccountActivationForm.php index 9cbec46..a569c1d 100644 --- a/api/models/authentication/RepeatAccountActivationForm.php +++ b/api/models/authentication/RepeatAccountActivationForm.php @@ -5,8 +5,8 @@ use api\aop\annotations\CollectModelMetrics; use api\components\ReCaptcha\Validator as ReCaptchaValidator; use api\exceptions\ThisShouldNotHappenException; use api\models\base\ApiForm; -use common\helpers\Error as E; use common\components\UserFriendlyRandomKey; +use common\helpers\Error as E; use common\models\Account; use common\models\confirmations\RegistrationConfirmation; use common\models\EmailActivation; diff --git a/api/modules/accounts/actions/BaseAccountAction.php b/api/modules/accounts/actions/BaseAccountAction.php index 6f637a4..149cf34 100644 --- a/api/modules/accounts/actions/BaseAccountAction.php +++ b/api/modules/accounts/actions/BaseAccountAction.php @@ -21,8 +21,6 @@ abstract class BaseAccountAction extends Action { return $this->formatSuccessResult($model); } - abstract protected function getFormClassName(): string; - public function getRequestData(): array { return Yii::$app->request->post(); } @@ -35,6 +33,8 @@ abstract class BaseAccountAction extends Action { return []; } + abstract protected function getFormClassName(): string; + private function formatFailedResult(AccountActionForm $model): array { $response = [ 'success' => false, diff --git a/api/modules/accounts/actions/ChangeEmailAction.php b/api/modules/accounts/actions/ChangeEmailAction.php index 241c7ed..ddb26ad 100644 --- a/api/modules/accounts/actions/ChangeEmailAction.php +++ b/api/modules/accounts/actions/ChangeEmailAction.php @@ -6,10 +6,6 @@ use api\modules\accounts\models\ChangeEmailForm; class ChangeEmailAction extends BaseAccountAction { - protected function getFormClassName(): string { - return ChangeEmailForm::class; - } - /** * @param ChangeEmailForm|AccountActionForm $model * @return array @@ -20,4 +16,8 @@ class ChangeEmailAction extends BaseAccountAction { ]; } + protected function getFormClassName(): string { + return ChangeEmailForm::class; + } + } diff --git a/api/modules/accounts/actions/EmailVerificationAction.php b/api/modules/accounts/actions/EmailVerificationAction.php index 76ff768..d03efdf 100644 --- a/api/modules/accounts/actions/EmailVerificationAction.php +++ b/api/modules/accounts/actions/EmailVerificationAction.php @@ -7,10 +7,6 @@ use common\helpers\Error as E; class EmailVerificationAction extends BaseAccountAction { - protected function getFormClassName(): string { - return SendEmailVerificationForm::class; - } - /** * @param SendEmailVerificationForm|AccountActionForm $model * @return array @@ -29,4 +25,8 @@ class EmailVerificationAction extends BaseAccountAction { ]; } + protected function getFormClassName(): string { + return SendEmailVerificationForm::class; + } + } diff --git a/api/modules/accounts/models/ChangeEmailForm.php b/api/modules/accounts/models/ChangeEmailForm.php index c1c92c4..1f03b67 100644 --- a/api/modules/accounts/models/ChangeEmailForm.php +++ b/api/modules/accounts/models/ChangeEmailForm.php @@ -49,7 +49,7 @@ class ChangeEmailForm extends AccountActionForm { } public function createTask(int $accountId, string $newEmail, string $oldEmail): void { - $model = new EmailChanged; + $model = new EmailChanged(); $model->accountId = $accountId; $model->oldEmail = $oldEmail; $model->newEmail = $newEmail; diff --git a/api/modules/accounts/models/TotpHelper.php b/api/modules/accounts/models/TotpHelper.php index 0f3111b..830667c 100644 --- a/api/modules/accounts/models/TotpHelper.php +++ b/api/modules/accounts/models/TotpHelper.php @@ -6,6 +6,8 @@ use OTPHP\TOTP; trait TotpHelper { + abstract public function getAccount(): Account; + protected function getTotp(): TOTP { $account = $this->getAccount(); $totp = TOTP::create($account->otp_secret); @@ -15,6 +17,4 @@ trait TotpHelper { return $totp; } - abstract public function getAccount(): Account; - } diff --git a/api/modules/authserver/controllers/AuthenticationController.php b/api/modules/authserver/controllers/AuthenticationController.php index 0f514a1..728859b 100644 --- a/api/modules/authserver/controllers/AuthenticationController.php +++ b/api/modules/authserver/controllers/AuthenticationController.php @@ -17,10 +17,10 @@ class AuthenticationController extends Controller { public function verbs() { return [ 'authenticate' => ['POST'], - 'refresh' => ['POST'], - 'validate' => ['POST'], - 'signout' => ['POST'], - 'invalidate' => ['POST'], + 'refresh' => ['POST'], + 'validate' => ['POST'], + 'signout' => ['POST'], + 'invalidate' => ['POST'], ]; } diff --git a/api/modules/authserver/models/AuthenticateData.php b/api/modules/authserver/models/AuthenticateData.php index 8f8cdea..68e373a 100644 --- a/api/modules/authserver/models/AuthenticateData.php +++ b/api/modules/authserver/models/AuthenticateData.php @@ -14,11 +14,11 @@ class AuthenticateData { $this->minecraftAccessKey = $minecraftAccessKey; } - public function getMinecraftAccessKey() : MinecraftAccessKey { + public function getMinecraftAccessKey(): MinecraftAccessKey { return $this->minecraftAccessKey; } - public function getResponseData(bool $includeAvailableProfiles = false) : array { + public function getResponseData(bool $includeAvailableProfiles = false): array { $accessKey = $this->minecraftAccessKey; $account = $accessKey->account; diff --git a/api/modules/authserver/models/AuthenticationForm.php b/api/modules/authserver/models/AuthenticationForm.php index b8c8921..5bacab6 100644 --- a/api/modules/authserver/models/AuthenticationForm.php +++ b/api/modules/authserver/models/AuthenticationForm.php @@ -14,7 +14,9 @@ use common\models\MinecraftAccessKey; class AuthenticationForm extends ApiForm { public $username; + public $password; + public $clientToken; public function rules() { @@ -41,13 +43,15 @@ class AuthenticationForm extends ApiForm { if (isset($errors['totp'])) { Authserver::error("User with login = '{$this->username}' protected by two factor auth."); throw new ForbiddenOperationException('Account protected with two factor auth.'); - } elseif (isset($errors['login'])) { + } + + if (isset($errors['login'])) { if ($errors['login'] === E::ACCOUNT_BANNED) { Authserver::error("User with login = '{$this->username}' is banned"); throw new ForbiddenOperationException('This account has been suspended.'); - } else { - Authserver::error("Cannot find user by login = '{$this->username}'"); } + + Authserver::error("Cannot find user by login = '{$this->username}'"); } elseif (isset($errors['password'])) { Authserver::error("User with login = '{$this->username}' passed wrong password."); } @@ -72,7 +76,7 @@ class AuthenticationForm extends ApiForm { return $dataModel; } - protected function createMinecraftAccessToken(Account $account) : MinecraftAccessKey { + protected function createMinecraftAccessToken(Account $account): MinecraftAccessKey { /** @var MinecraftAccessKey|null $accessTokenModel */ $accessTokenModel = MinecraftAccessKey::findOne([ 'account_id' => $account->id, @@ -92,7 +96,7 @@ class AuthenticationForm extends ApiForm { return $accessTokenModel; } - protected function createLoginForm() : LoginForm { + protected function createLoginForm(): LoginForm { return new LoginForm(); } diff --git a/api/modules/authserver/models/InvalidateForm.php b/api/modules/authserver/models/InvalidateForm.php index a81e0ef..d270c7f 100644 --- a/api/modules/authserver/models/InvalidateForm.php +++ b/api/modules/authserver/models/InvalidateForm.php @@ -8,6 +8,7 @@ use common\models\MinecraftAccessKey; class InvalidateForm extends ApiForm { public $accessToken; + public $clientToken; public function rules() { @@ -20,7 +21,7 @@ class InvalidateForm extends ApiForm { * @return bool * @throws \api\modules\authserver\exceptions\AuthserverException */ - public function invalidateToken() : bool { + public function invalidateToken(): bool { $this->validate(); $token = MinecraftAccessKey::findOne([ diff --git a/api/modules/authserver/models/RefreshTokenForm.php b/api/modules/authserver/models/RefreshTokenForm.php index 063e324..2deb3b1 100644 --- a/api/modules/authserver/models/RefreshTokenForm.php +++ b/api/modules/authserver/models/RefreshTokenForm.php @@ -10,6 +10,7 @@ use common\models\MinecraftAccessKey; class RefreshTokenForm extends ApiForm { public $accessToken; + public $clientToken; public function rules() { diff --git a/api/modules/authserver/models/SignoutForm.php b/api/modules/authserver/models/SignoutForm.php index f1602c2..7f4b1d7 100644 --- a/api/modules/authserver/models/SignoutForm.php +++ b/api/modules/authserver/models/SignoutForm.php @@ -12,6 +12,7 @@ use Yii; class SignoutForm extends ApiForm { public $username; + public $password; public function rules() { @@ -20,7 +21,7 @@ class SignoutForm extends ApiForm { ]; } - public function signout() : bool { + public function signout(): bool { $this->validate(); $loginForm = new LoginForm(); diff --git a/api/modules/authserver/models/ValidateForm.php b/api/modules/authserver/models/ValidateForm.php index 2e0a26e..75370e2 100644 --- a/api/modules/authserver/models/ValidateForm.php +++ b/api/modules/authserver/models/ValidateForm.php @@ -16,7 +16,7 @@ class ValidateForm extends ApiForm { ]; } - public function validateToken() : bool { + public function validateToken(): bool { $this->validate(); /** @var MinecraftAccessKey|null $result */ diff --git a/api/modules/mojang/controllers/ApiController.php b/api/modules/mojang/controllers/ApiController.php index 680fb7f..a7b19b2 100644 --- a/api/modules/mojang/controllers/ApiController.php +++ b/api/modules/mojang/controllers/ApiController.php @@ -54,7 +54,7 @@ class ApiController extends Controller { public function actionUsernamesByUuid($uuid) { try { $uuid = Uuid::fromString($uuid)->toString(); - } catch(\InvalidArgumentException $e) { + } catch (\InvalidArgumentException $e) { return $this->illegalArgumentResponse('Invalid uuid format.'); } @@ -69,7 +69,7 @@ class ApiController extends Controller { ->all(); $data = []; - foreach($usernameHistory as $record) { + foreach ($usernameHistory as $record) { $data[] = [ 'name' => $record->username, 'changedToAt' => $record->applied_in * 1000, @@ -94,7 +94,7 @@ class ApiController extends Controller { return $this->illegalArgumentResponse('Not more that 100 profile name per call is allowed.'); } - foreach($usernames as $username) { + foreach ($usernames as $username) { if (empty($username) || is_array($username)) { return $this->illegalArgumentResponse('profileName can not be null, empty or array key.'); } @@ -108,7 +108,7 @@ class ApiController extends Controller { ->all(); $responseData = []; - foreach($accounts as $account) { + foreach ($accounts as $account) { $responseData[] = [ 'id' => str_replace('-', '', $account->uuid), 'name' => $account->username, diff --git a/api/modules/oauth/controllers/AuthorizationController.php b/api/modules/oauth/controllers/AuthorizationController.php index aceb385..d0ad35c 100644 --- a/api/modules/oauth/controllers/AuthorizationController.php +++ b/api/modules/oauth/controllers/AuthorizationController.php @@ -38,7 +38,7 @@ class AuthorizationController extends Controller { return [ 'validate' => ['GET'], 'complete' => ['POST'], - 'token' => ['POST'], + 'token' => ['POST'], ]; } diff --git a/api/modules/oauth/exceptions/OauthException.php b/api/modules/oauth/exceptions/OauthException.php index 775c0a7..3197b75 100644 --- a/api/modules/oauth/exceptions/OauthException.php +++ b/api/modules/oauth/exceptions/OauthException.php @@ -4,4 +4,5 @@ declare(strict_types=1); namespace api\modules\oauth\exceptions; interface OauthException { + } diff --git a/api/modules/oauth/models/ApplicationType.php b/api/modules/oauth/models/ApplicationType.php index 244bf7c..c4745a2 100644 --- a/api/modules/oauth/models/ApplicationType.php +++ b/api/modules/oauth/models/ApplicationType.php @@ -16,7 +16,7 @@ class ApplicationType extends BaseOauthClientType { public function rules(): array { return ArrayHelper::merge(parent::rules(), [ ['redirectUri', 'required', 'message' => E::REDIRECT_URI_REQUIRED], - ['redirectUri', 'url', 'validSchemes' => ['[\w]+'], 'message' => E::REDIRECT_URI_INVALID], + ['redirectUri', 'url', 'validSchemes' => ['[\w]+'], 'message' => E::REDIRECT_URI_INVALID], ['description', 'string'], ]); } diff --git a/api/modules/session/controllers/SessionController.php b/api/modules/session/controllers/SessionController.php index 4c860ba..6e88b11 100644 --- a/api/modules/session/controllers/SessionController.php +++ b/api/modules/session/controllers/SessionController.php @@ -98,7 +98,7 @@ class SessionController extends Controller { public function actionProfile($uuid) { try { $uuid = Uuid::fromString($uuid)->toString(); - } catch(\InvalidArgumentException $e) { + } catch (\InvalidArgumentException $e) { throw new IllegalArgumentException('Invalid uuid format.'); } diff --git a/api/modules/session/exceptions/ForbiddenOperationException.php b/api/modules/session/exceptions/ForbiddenOperationException.php index 420aaec..2a389e4 100644 --- a/api/modules/session/exceptions/ForbiddenOperationException.php +++ b/api/modules/session/exceptions/ForbiddenOperationException.php @@ -1,7 +1,7 @@ server; } - protected function buildKey($ip) : string { + protected function buildKey($ip): string { return 'sessionserver:ratelimit:' . $ip; } diff --git a/api/modules/session/models/JoinForm.php b/api/modules/session/models/JoinForm.php index 8b47053..0361ae5 100644 --- a/api/modules/session/models/JoinForm.php +++ b/api/modules/session/models/JoinForm.php @@ -7,9 +7,9 @@ use api\modules\session\models\protocols\JoinInterface; use api\modules\session\Module as Session; use api\modules\session\validators\RequiredValidator; use common\helpers\StringHelper; -use common\rbac\Permissions as P; use common\models\Account; use common\models\MinecraftAccessKey; +use common\rbac\Permissions as P; use Ramsey\Uuid\Uuid; use Yii; use yii\base\ErrorException; @@ -19,7 +19,9 @@ use yii\web\UnauthorizedHttpException; class JoinForm extends Model { public $accessToken; + public $selectedProfile; + public $serverId; /** diff --git a/api/modules/session/models/SessionModel.php b/api/modules/session/models/SessionModel.php index c58c65e..ed5e626 100644 --- a/api/modules/session/models/SessionModel.php +++ b/api/modules/session/models/SessionModel.php @@ -6,7 +6,7 @@ use Yii; class SessionModel { - const KEY_TIME = 120; // 2 min + private const KEY_TIME = 120; // 2 min public $username; diff --git a/api/modules/session/models/protocols/BaseHasJoined.php b/api/modules/session/models/protocols/BaseHasJoined.php index 2bce25e..99e5d28 100644 --- a/api/modules/session/models/protocols/BaseHasJoined.php +++ b/api/modules/session/models/protocols/BaseHasJoined.php @@ -4,6 +4,7 @@ namespace api\modules\session\models\protocols; abstract class BaseHasJoined implements HasJoinedInterface { private $username; + private $serverId; public function __construct(string $username, string $serverId) { diff --git a/api/modules/session/models/protocols/LegacyJoin.php b/api/modules/session/models/protocols/LegacyJoin.php index 16ee65e..6b119ff 100644 --- a/api/modules/session/models/protocols/LegacyJoin.php +++ b/api/modules/session/models/protocols/LegacyJoin.php @@ -4,10 +4,13 @@ namespace api\modules\session\models\protocols; class LegacyJoin extends BaseJoin { private $user; + private $sessionId; + private $serverId; private $accessToken; + private $uuid; public function __construct(string $user, string $sessionId, string $serverId) { @@ -18,7 +21,7 @@ class LegacyJoin extends BaseJoin { $this->parseSessionId($this->sessionId); } - public function getAccessToken() : string { + public function getAccessToken(): string { return $this->accessToken; } diff --git a/api/modules/session/models/protocols/ModernJoin.php b/api/modules/session/models/protocols/ModernJoin.php index 6fd0942..493bc21 100644 --- a/api/modules/session/models/protocols/ModernJoin.php +++ b/api/modules/session/models/protocols/ModernJoin.php @@ -4,7 +4,9 @@ namespace api\modules\session\models\protocols; class ModernJoin extends BaseJoin { private $accessToken; + private $selectedProfile; + private $serverId; public function __construct(string $accessToken, string $selectedProfile, string $serverId) { diff --git a/api/traits/AccountFinder.php b/api/traits/AccountFinder.php index 782ba57..33a01b5 100644 --- a/api/traits/AccountFinder.php +++ b/api/traits/AccountFinder.php @@ -7,7 +7,7 @@ trait AccountFinder { private $account; - public abstract function getLogin(): string; + abstract public function getLogin(): string; public function getAccount(): ?Account { if ($this->account === null) { diff --git a/common/behaviors/DataBehavior.php b/common/behaviors/DataBehavior.php index 8c0c2e1..967cf85 100644 --- a/common/behaviors/DataBehavior.php +++ b/common/behaviors/DataBehavior.php @@ -5,6 +5,7 @@ use yii\base\Behavior; use yii\helpers\ArrayHelper; class DataBehavior extends Behavior { + /** * @var string имя атрибута, к которому будет применяться поведение */ diff --git a/common/behaviors/EmailActivationExpirationBehavior.php b/common/behaviors/EmailActivationExpirationBehavior.php index 3412455..d518437 100644 --- a/common/behaviors/EmailActivationExpirationBehavior.php +++ b/common/behaviors/EmailActivationExpirationBehavior.php @@ -30,7 +30,7 @@ class EmailActivationExpirationBehavior extends Behavior { * @see EmailActivation::compareTime() * @return bool */ - public function canRepeat() : bool { + public function canRepeat(): bool { return $this->compareTime($this->repeatTimeout); } @@ -44,7 +44,7 @@ class EmailActivationExpirationBehavior extends Behavior { * @see EmailActivation::compareTime() * @return bool */ - public function isExpired() : bool { + public function isExpired(): bool { return $this->compareTime($this->expirationTimeout); } @@ -53,7 +53,7 @@ class EmailActivationExpirationBehavior extends Behavior { * * @return int */ - public function canRepeatIn() : int { + public function canRepeatIn(): int { return $this->calculateTime($this->repeatTimeout); } @@ -62,11 +62,11 @@ class EmailActivationExpirationBehavior extends Behavior { * * @return int */ - public function expireIn() : int { + public function expireIn(): int { return $this->calculateTime($this->expirationTimeout); } - protected function compareTime(int $value) : bool { + protected function compareTime(int $value): bool { if ($value < 0) { return false; } @@ -78,7 +78,7 @@ class EmailActivationExpirationBehavior extends Behavior { return time() > $this->calculateTime($value); } - protected function calculateTime(int $value) : int { + protected function calculateTime(int $value): int { return $this->owner->created_at + $value; } diff --git a/common/behaviors/PrimaryKeyValueBehavior.php b/common/behaviors/PrimaryKeyValueBehavior.php index b882547..0882f20 100644 --- a/common/behaviors/PrimaryKeyValueBehavior.php +++ b/common/behaviors/PrimaryKeyValueBehavior.php @@ -24,7 +24,7 @@ class PrimaryKeyValueBehavior extends Behavior { ]; } - public function setPrimaryKeyValue() : bool { + public function setPrimaryKeyValue(): bool { if ($this->owner->getPrimaryKey() === null) { $this->refreshPrimaryKeyValue(); } @@ -40,15 +40,15 @@ class PrimaryKeyValueBehavior extends Behavior { $this->owner->{$this->getPrimaryKeyName()} = $key; } - protected function generateValue() : string { + protected function generateValue(): string { return (string)call_user_func($this->value); } - protected function isValueExists(string $key) : bool { + protected function isValueExists(string $key): bool { return $this->owner->find()->andWhere([$this->getPrimaryKeyName() => $key])->exists(); } - protected function getPrimaryKeyName() : string { + protected function getPrimaryKeyName(): string { $owner = $this->owner; $primaryKeys = $owner->primaryKey(); if (!isset($primaryKeys[0])) { diff --git a/common/components/EmailRenderer.php b/common/components/EmailRenderer.php index ce0e3e7..615906e 100644 --- a/common/components/EmailRenderer.php +++ b/common/components/EmailRenderer.php @@ -43,7 +43,7 @@ class EmailRenderer extends Component { $this->renderer->setBaseDomain($this->buildBasePath()); } - public function getBaseDomain() : string { + public function getBaseDomain(): string { return $this->_baseDomain; } diff --git a/common/components/Qr/ElyDecorator.php b/common/components/Qr/ElyDecorator.php index 6ce2f47..20876e8 100644 --- a/common/components/Qr/ElyDecorator.php +++ b/common/components/Qr/ElyDecorator.php @@ -106,7 +106,6 @@ class ElyDecorator implements DecoratorInterface { $topPadding, $multiple ) { - } private function encodeSvgToBase64(string $filePath): string { diff --git a/common/components/RabbitMQ/Component.php b/common/components/RabbitMQ/Component.php index 0ce0f91..b7e6177 100644 --- a/common/components/RabbitMQ/Component.php +++ b/common/components/RabbitMQ/Component.php @@ -1,11 +1,11 @@ basic_publish(...$this->preparePublishArgs($message, $exchangeName, $routingKey, $publishArgs)); } + /** + * Returns prepaired AMQP message. + * + * @param string|array|object $message + * @param array $properties + * @return AMQPMessage + * @throws Exception If message is empty. + */ + public function prepareMessage($message, $properties = null) { + if ($message instanceof AMQPMessage) { + return $message; + } + + if (empty($message)) { + throw new Exception('AMQP message can not be empty'); + } + + if (is_array($message) || is_object($message)) { + $message = Json::encode($message); + } + + return new AMQPMessage($message, $properties); + } + /** * Объединяет переданный набор аргументов с поведением по умолчанию * @@ -150,28 +174,4 @@ class Component extends \yii\base\Component { ], $args); } - /** - * Returns prepaired AMQP message. - * - * @param string|array|object $message - * @param array $properties - * @return AMQPMessage - * @throws Exception If message is empty. - */ - public function prepareMessage($message, $properties = null) { - if ($message instanceof AMQPMessage) { - return $message; - } - - if (empty($message)) { - throw new Exception('AMQP message can not be empty'); - } - - if (is_array($message) || is_object($message)) { - $message = Json::encode($message); - } - - return new AMQPMessage($message, $properties); - } - } diff --git a/common/components/Redis/Connection.php b/common/components/Redis/Connection.php index 5bdc47d..28d62c4 100644 --- a/common/components/Redis/Connection.php +++ b/common/components/Redis/Connection.php @@ -157,7 +157,7 @@ class Connection extends Component implements ConnectionInterface { /** * @var array List of available redis commands http://redis.io/commands */ - const REDIS_COMMANDS = [ + public const REDIS_COMMANDS = [ 'BLPOP', // key [key ...] timeout Remove and get the first element in a list, or block until one is available 'BRPOP', // key [key ...] timeout Remove and get the last element in a list, or block until one is available 'BRPOPLPUSH', // source destination timeout Pop a value from a list, push it to another list and return it; or block until one is available @@ -368,14 +368,6 @@ class Connection extends Component implements ConnectionInterface { */ private $_client; - public function getConnection() : ClientInterface { - if ($this->_client === null) { - $this->_client = new Client($this->prepareParams(), $this->options); - } - - return $this->_client; - } - public function __call($name, $params) { $redisCommand = mb_strtoupper($name); if (in_array($redisCommand, self::REDIS_COMMANDS)) { @@ -385,6 +377,14 @@ class Connection extends Component implements ConnectionInterface { return parent::__call($name, $params); } + public function getConnection(): ClientInterface { + if ($this->_client === null) { + $this->_client = new Client($this->prepareParams(), $this->options); + } + + return $this->_client; + } + public function executeCommand(string $name, array $params = []) { return $this->getConnection()->$name(...$params); } diff --git a/common/components/Redis/Key.php b/common/components/Redis/Key.php index 2f38b95..3ce7e15 100644 --- a/common/components/Redis/Key.php +++ b/common/components/Redis/Key.php @@ -30,11 +30,13 @@ class Key { public function setValue($value): self { $this->getRedis()->set($this->key, $value); + return $this; } public function delete(): self { $this->getRedis()->del([$this->getKey()]); + return $this; } @@ -44,17 +46,19 @@ class Key { public function expire(int $ttl): self { $this->getRedis()->expire($this->key, $ttl); + return $this; } public function expireAt(int $unixTimestamp): self { $this->getRedis()->expireat($this->key, $unixTimestamp); + return $this; } private function buildKey(array $parts): string { $keyParts = []; - foreach($parts as $part) { + foreach ($parts as $part) { $keyParts[] = str_replace('_', ':', $part); } diff --git a/common/components/Redis/Set.php b/common/components/Redis/Set.php index debae64..b106304 100644 --- a/common/components/Redis/Set.php +++ b/common/components/Redis/Set.php @@ -8,11 +8,13 @@ class Set extends Key implements IteratorAggregate { public function add($value): self { $this->getRedis()->sadd($this->getKey(), $value); + return $this; } public function remove($value): self { $this->getRedis()->srem($this->getKey(), $value); + return $this; } diff --git a/common/components/SkinSystem/Api.php b/common/components/SkinSystem/Api.php index 4237eac..4854354 100644 --- a/common/components/SkinSystem/Api.php +++ b/common/components/SkinSystem/Api.php @@ -6,7 +6,7 @@ use Yii; class Api { - const BASE_DOMAIN = 'http://skinsystem.ely.by'; + private const BASE_DOMAIN = 'http://skinsystem.ely.by'; /** * @param string $username diff --git a/common/components/UserFriendlyRandomKey.php b/common/components/UserFriendlyRandomKey.php index ebeb111..6c66c02 100644 --- a/common/components/UserFriendlyRandomKey.php +++ b/common/components/UserFriendlyRandomKey.php @@ -1,10 +1,11 @@ application = $application; } - public function getEnvironment() : string { + public function getEnvironment(): string { return YII_ENV; } - public function getConfig() : array { + public function getConfig(): array { $toMerge = [ require __DIR__ . '/config.php', ]; @@ -55,7 +55,7 @@ class ConfigLoader { return ArrayHelper::merge(...$toMerge); } - public static function load(string $application) : array { + public static function load(string $application): array { return (new static($application))->getConfig(); } diff --git a/common/config/config.php b/common/config/config.php index c8f3632..36b1266 100644 --- a/common/config/config.php +++ b/common/config/config.php @@ -115,6 +115,6 @@ return [ ], 'aliases' => [ '@bower' => '@vendor/bower-asset', - '@npm' => '@vendor/npm-asset', + '@npm' => '@vendor/npm-asset', ], ]; diff --git a/common/db/mysql/QueryBuilder.php b/common/db/mysql/QueryBuilder.php index f467396..66c5a24 100644 --- a/common/db/mysql/QueryBuilder.php +++ b/common/db/mysql/QueryBuilder.php @@ -1,7 +1,7 @@ $direction) { - if ($direction instanceof Expression) { + foreach ($columns as $name => $direction) { + if ($direction instanceof ExpressionInterface) { $orders[] = $direction->expression; } elseif (is_array($direction)) { - // This is new feature + // This condition branch is our custom solution if (empty($direction)) { continue; } $fieldValues = []; - foreach($direction as $fieldValue) { + foreach ($direction as $fieldValue) { $fieldValues[] = $this->db->quoteValue($fieldValue); } $orders[] = 'FIELD(' . $this->db->quoteColumnName($name) . ',' . implode(',', $fieldValues) . ')'; - // End of new feature } else { $orders[] = $this->db->quoteColumnName($name) . ($direction === SORT_DESC ? ' DESC' : ''); } diff --git a/common/emails/TemplateWithRenderer.php b/common/emails/TemplateWithRenderer.php index 33b40f1..adfef8c 100644 --- a/common/emails/TemplateWithRenderer.php +++ b/common/emails/TemplateWithRenderer.php @@ -42,7 +42,7 @@ abstract class TemplateWithRenderer extends Template { */ abstract public function getTemplateName(): string; - protected final function getView() { + final protected function getView() { return $this->getTemplateName(); } diff --git a/common/emails/templates/ChangeEmailConfirmCurrentEmail.php b/common/emails/templates/ChangeEmailConfirmCurrentEmail.php index 8c48907..ea280dd 100644 --- a/common/emails/templates/ChangeEmailConfirmCurrentEmail.php +++ b/common/emails/templates/ChangeEmailConfirmCurrentEmail.php @@ -16,6 +16,12 @@ class ChangeEmailConfirmCurrentEmail extends Template { return 'Ely.by Account change E-mail confirmation'; } + public function getParams(): array { + return [ + 'key' => $this->key, + ]; + } + /** * @return string|array */ @@ -26,10 +32,4 @@ class ChangeEmailConfirmCurrentEmail extends Template { ]; } - public function getParams(): array { - return [ - 'key' => $this->key, - ]; - } - } diff --git a/common/emails/templates/ChangeEmailConfirmNewEmail.php b/common/emails/templates/ChangeEmailConfirmNewEmail.php index 507b830..ea1dd9b 100644 --- a/common/emails/templates/ChangeEmailConfirmNewEmail.php +++ b/common/emails/templates/ChangeEmailConfirmNewEmail.php @@ -19,6 +19,13 @@ class ChangeEmailConfirmNewEmail extends Template { return 'Ely.by Account new E-mail confirmation'; } + public function getParams(): array { + return [ + 'key' => $this->key, + 'username' => $this->username, + ]; + } + /** * @return string|array */ @@ -29,11 +36,4 @@ class ChangeEmailConfirmNewEmail extends Template { ]; } - public function getParams(): array { - return [ - 'key' => $this->key, - 'username' => $this->username, - ]; - } - } diff --git a/common/helpers/Error.php b/common/helpers/Error.php index 8e34ba9..1d34bfc 100644 --- a/common/helpers/Error.php +++ b/common/helpers/Error.php @@ -3,70 +3,70 @@ namespace common\helpers; final class Error { - const USERNAME_REQUIRED = 'error.username_required'; - const USERNAME_TOO_SHORT = 'error.username_too_short'; - const USERNAME_TOO_LONG = 'error.username_too_long'; - const USERNAME_INVALID = 'error.username_invalid'; - const USERNAME_NOT_AVAILABLE = 'error.username_not_available'; + public const USERNAME_REQUIRED = 'error.username_required'; + public const USERNAME_TOO_SHORT = 'error.username_too_short'; + public const USERNAME_TOO_LONG = 'error.username_too_long'; + public const USERNAME_INVALID = 'error.username_invalid'; + public const USERNAME_NOT_AVAILABLE = 'error.username_not_available'; - const EMAIL_REQUIRED = 'error.email_required'; - const EMAIL_TOO_LONG = 'error.email_too_long'; - const EMAIL_INVALID = 'error.email_invalid'; - const EMAIL_IS_TEMPMAIL = 'error.email_is_tempmail'; - const EMAIL_NOT_AVAILABLE = 'error.email_not_available'; - const EMAIL_NOT_FOUND = 'error.email_not_found'; + public const EMAIL_REQUIRED = 'error.email_required'; + public const EMAIL_TOO_LONG = 'error.email_too_long'; + public const EMAIL_INVALID = 'error.email_invalid'; + public const EMAIL_IS_TEMPMAIL = 'error.email_is_tempmail'; + public const EMAIL_NOT_AVAILABLE = 'error.email_not_available'; + public const EMAIL_NOT_FOUND = 'error.email_not_found'; - const LOGIN_REQUIRED = 'error.login_required'; - const LOGIN_NOT_EXIST = 'error.login_not_exist'; + public const LOGIN_REQUIRED = 'error.login_required'; + public const LOGIN_NOT_EXIST = 'error.login_not_exist'; - const PASSWORD_REQUIRED = 'error.password_required'; - const PASSWORD_INCORRECT = 'error.password_incorrect'; - const PASSWORD_TOO_SHORT = 'error.password_too_short'; + public const PASSWORD_REQUIRED = 'error.password_required'; + public const PASSWORD_INCORRECT = 'error.password_incorrect'; + public const PASSWORD_TOO_SHORT = 'error.password_too_short'; - const KEY_REQUIRED = 'error.key_required'; - const KEY_NOT_EXISTS = 'error.key_not_exists'; - const KEY_EXPIRE = 'error.key_expire'; + public const KEY_REQUIRED = 'error.key_required'; + public const KEY_NOT_EXISTS = 'error.key_not_exists'; + public const KEY_EXPIRE = 'error.key_expire'; - const ACCOUNT_BANNED = 'error.account_banned'; - const ACCOUNT_NOT_ACTIVATED = 'error.account_not_activated'; - const ACCOUNT_ALREADY_ACTIVATED = 'error.account_already_activated'; - const ACCOUNT_CANNOT_RESEND_MESSAGE = 'error.account_cannot_resend_message'; + public const ACCOUNT_BANNED = 'error.account_banned'; + public const ACCOUNT_NOT_ACTIVATED = 'error.account_not_activated'; + public const ACCOUNT_ALREADY_ACTIVATED = 'error.account_already_activated'; + public const ACCOUNT_CANNOT_RESEND_MESSAGE = 'error.account_cannot_resend_message'; - const RECENTLY_SENT_MESSAGE = 'error.recently_sent_message'; + public const RECENTLY_SENT_MESSAGE = 'error.recently_sent_message'; - const NEW_PASSWORD_REQUIRED = 'error.newPassword_required'; - const NEW_RE_PASSWORD_REQUIRED = 'error.newRePassword_required'; - const NEW_RE_PASSWORD_DOES_NOT_MATCH = self::RE_PASSWORD_DOES_NOT_MATCH; + public const NEW_PASSWORD_REQUIRED = 'error.newPassword_required'; + public const NEW_RE_PASSWORD_REQUIRED = 'error.newRePassword_required'; + public const NEW_RE_PASSWORD_DOES_NOT_MATCH = self::RE_PASSWORD_DOES_NOT_MATCH; - const REFRESH_TOKEN_REQUIRED = 'error.refresh_token_required'; - const REFRESH_TOKEN_NOT_EXISTS = 'error.refresh_token_not_exist'; + public const REFRESH_TOKEN_REQUIRED = 'error.refresh_token_required'; + public const REFRESH_TOKEN_NOT_EXISTS = 'error.refresh_token_not_exist'; - const CAPTCHA_REQUIRED = 'error.captcha_required'; - const CAPTCHA_INVALID = 'error.captcha_invalid'; + public const CAPTCHA_REQUIRED = 'error.captcha_required'; + public const CAPTCHA_INVALID = 'error.captcha_invalid'; - const RULES_AGREEMENT_REQUIRED = 'error.rulesAgreement_required'; + public const RULES_AGREEMENT_REQUIRED = 'error.rulesAgreement_required'; - const RE_PASSWORD_REQUIRED = 'error.rePassword_required'; - const RE_PASSWORD_DOES_NOT_MATCH = 'error.rePassword_does_not_match'; + public const RE_PASSWORD_REQUIRED = 'error.rePassword_required'; + public const RE_PASSWORD_DOES_NOT_MATCH = 'error.rePassword_does_not_match'; - const UNSUPPORTED_LANGUAGE = 'error.unsupported_language'; + public const UNSUPPORTED_LANGUAGE = 'error.unsupported_language'; - const SUBJECT_REQUIRED = 'error.subject_required'; - const MESSAGE_REQUIRED = 'error.message_required'; + public const SUBJECT_REQUIRED = 'error.subject_required'; + public const MESSAGE_REQUIRED = 'error.message_required'; - const TOTP_REQUIRED = 'error.totp_required'; - const TOTP_INCORRECT = 'error.totp_incorrect'; + public const TOTP_REQUIRED = 'error.totp_required'; + public const TOTP_INCORRECT = 'error.totp_incorrect'; - const OTP_ALREADY_ENABLED = 'error.otp_already_enabled'; - const OTP_NOT_ENABLED = 'error.otp_not_enabled'; + public const OTP_ALREADY_ENABLED = 'error.otp_already_enabled'; + public const OTP_NOT_ENABLED = 'error.otp_not_enabled'; - const NAME_REQUIRED = 'error.name_required'; + public const NAME_REQUIRED = 'error.name_required'; - const REDIRECT_URI_REQUIRED = 'error.redirectUri_required'; - const REDIRECT_URI_INVALID = 'error.redirectUri_invalid'; + public const REDIRECT_URI_REQUIRED = 'error.redirectUri_required'; + public const REDIRECT_URI_INVALID = 'error.redirectUri_invalid'; - const WEBSITE_URL_INVALID = 'error.websiteUrl_invalid'; + public const WEBSITE_URL_INVALID = 'error.websiteUrl_invalid'; - const MINECRAFT_SERVER_IP_INVALID = 'error.minecraftServerIp_invalid'; + public const MINECRAFT_SERVER_IP_INVALID = 'error.minecraftServerIp_invalid'; } diff --git a/common/helpers/StringHelper.php b/common/helpers/StringHelper.php index 97be7b1..1e29e61 100644 --- a/common/helpers/StringHelper.php +++ b/common/helpers/StringHelper.php @@ -13,9 +13,9 @@ class StringHelper { if ($usernameLength === 1) { $mask = $maskChars; - } elseif($usernameLength === 2) { + } elseif ($usernameLength === 2) { $mask = mb_substr($username, 0, 1) . $maskChars; - } elseif($usernameLength === 3) { + } elseif ($usernameLength === 3) { $mask = mb_substr($username, 0, 1) . $maskChars . mb_substr($username, 2, 1); } else { $mask = mb_substr($username, 0, 2) . $maskChars . mb_substr($username, -2, 2); diff --git a/common/models/Account.php b/common/models/Account.php index a923a63..06d1961 100644 --- a/common/models/Account.php +++ b/common/models/Account.php @@ -44,13 +44,13 @@ use const common\LATEST_RULES_VERSION; */ class Account extends ActiveRecord { - const STATUS_DELETED = -10; - const STATUS_BANNED = -1; - const STATUS_REGISTERED = 0; - const STATUS_ACTIVE = 10; + public const STATUS_DELETED = -10; + public const STATUS_BANNED = -1; + public const STATUS_REGISTERED = 0; + public const STATUS_ACTIVE = 10; - const PASS_HASH_STRATEGY_OLD_ELY = 0; - const PASS_HASH_STRATEGY_YII2 = 1; + public const PASS_HASH_STRATEGY_OLD_ELY = 0; + public const PASS_HASH_STRATEGY_YII2 = 1; public static function tableName(): string { return '{{%accounts}}'; @@ -67,10 +67,9 @@ class Account extends ActiveRecord { $passwordHashStrategy = $this->password_hash_strategy; } - switch($passwordHashStrategy) { + switch ($passwordHashStrategy) { case self::PASS_HASH_STRATEGY_OLD_ELY: - $hashedPass = UserPass::make($this->email, $password); - return $hashedPass === $this->password_hash; + return UserPass::make($this->email, $password) === $this->password_hash; case self::PASS_HASH_STRATEGY_YII2: return Yii::$app->security->validatePassword($password, $this->password_hash); diff --git a/common/models/EmailActivation.php b/common/models/EmailActivation.php index 0b53bf7..b71e4e8 100644 --- a/common/models/EmailActivation.php +++ b/common/models/EmailActivation.php @@ -28,10 +28,10 @@ use yii\helpers\ArrayHelper; */ class EmailActivation extends ActiveRecord { - const TYPE_REGISTRATION_EMAIL_CONFIRMATION = 0; - const TYPE_FORGOT_PASSWORD_KEY = 1; - const TYPE_CURRENT_EMAIL_CONFIRMATION = 2; - const TYPE_NEW_EMAIL_CONFIRMATION = 3; + public const TYPE_REGISTRATION_EMAIL_CONFIRMATION = 0; + public const TYPE_FORGOT_PASSWORD_KEY = 1; + public const TYPE_CURRENT_EMAIL_CONFIRMATION = 2; + public const TYPE_NEW_EMAIL_CONFIRMATION = 3; public static function tableName() { return '{{%email_activations}}'; @@ -79,15 +79,15 @@ class EmailActivation extends ActiveRecord { throw new InvalidConfigException('Unexpected type'); } - return new $classMap[$type]; + return new $classMap[$type](); } public static function getClassMap() { return [ self::TYPE_REGISTRATION_EMAIL_CONFIRMATION => confirmations\RegistrationConfirmation::class, - self::TYPE_FORGOT_PASSWORD_KEY => confirmations\ForgotPassword::class, - self::TYPE_CURRENT_EMAIL_CONFIRMATION => confirmations\CurrentEmailConfirmation::class, - self::TYPE_NEW_EMAIL_CONFIRMATION => confirmations\NewEmailConfirmation::class, + self::TYPE_FORGOT_PASSWORD_KEY => confirmations\ForgotPassword::class, + self::TYPE_CURRENT_EMAIL_CONFIRMATION => confirmations\CurrentEmailConfirmation::class, + self::TYPE_NEW_EMAIL_CONFIRMATION => confirmations\NewEmailConfirmation::class, ]; } diff --git a/common/models/MinecraftAccessKey.php b/common/models/MinecraftAccessKey.php index 57294fa..202570a 100644 --- a/common/models/MinecraftAccessKey.php +++ b/common/models/MinecraftAccessKey.php @@ -29,7 +29,7 @@ use yii\db\ActiveRecord; */ class MinecraftAccessKey extends ActiveRecord { - const LIFETIME = 172800; // Ключ актуален в течение 2 дней + public const LIFETIME = 172800; // Ключ актуален в течение 2 дней public static function tableName(): string { return '{{%minecraft_access_keys}}'; diff --git a/common/models/OauthScopeQuery.php b/common/models/OauthScopeQuery.php index 27577c5..b81730d 100644 --- a/common/models/OauthScopeQuery.php +++ b/common/models/OauthScopeQuery.php @@ -11,6 +11,10 @@ class OauthScopeQuery { private $owner; + public function __construct(array $scopes) { + $this->scopes = $scopes; + } + public function onlyPublic(): self { $this->internal = false; return $this; @@ -43,8 +47,4 @@ class OauthScopeQuery { }), 'value'); } - public function __construct(array $scopes) { - $this->scopes = $scopes; - } - } diff --git a/common/models/Textures.php b/common/models/Textures.php index 85ec58c..6046fff 100644 --- a/common/models/Textures.php +++ b/common/models/Textures.php @@ -21,7 +21,7 @@ class Textures { } public function getMinecraftResponse() { - $response = [ + $response = [ 'name' => $this->account->username, 'id' => str_replace('-', '', $this->account->uuid), 'properties' => [ @@ -54,9 +54,9 @@ class Textures { if (!$encrypted) { return $array; - } else { - return static::encrypt($array); } + + return static::encrypt($array); } public function getTextures(): array { diff --git a/common/models/UsernameHistory.php b/common/models/UsernameHistory.php index 720cc10..bba421b 100644 --- a/common/models/UsernameHistory.php +++ b/common/models/UsernameHistory.php @@ -45,7 +45,7 @@ class UsernameHistory extends ActiveRecord { * @param int $afterTime * @return UsernameHistory|null */ - public function findNext(int $afterTime = null) /*: ?UsernameHistory*/ { + public function findNext(int $afterTime = null): ?UsernameHistory { return self::find() ->andWhere(['account_id' => $this->account_id]) ->andWhere(['>', 'applied_in', $afterTime ?: $this->applied_in]) diff --git a/common/models/confirmations/NewEmailConfirmation.php b/common/models/confirmations/NewEmailConfirmation.php index 91f0fab..a8e4177 100644 --- a/common/models/confirmations/NewEmailConfirmation.php +++ b/common/models/confirmations/NewEmailConfirmation.php @@ -13,7 +13,7 @@ class NewEmailConfirmation extends EmailActivation { public function behaviors() { return ArrayHelper::merge(parent::behaviors(), [ 'expirationBehavior' => [ - 'repeatTimeout' => 5 * 60, + 'repeatTimeout' => 5 * 60, ], 'dataBehavior' => [ 'class' => NewEmailConfirmationBehavior::class, diff --git a/common/models/confirmations/NewEmailConfirmationBehavior.php b/common/models/confirmations/NewEmailConfirmationBehavior.php index ee804a5..169d88c 100644 --- a/common/models/confirmations/NewEmailConfirmationBehavior.php +++ b/common/models/confirmations/NewEmailConfirmationBehavior.php @@ -8,11 +8,11 @@ use common\behaviors\DataBehavior; */ class NewEmailConfirmationBehavior extends DataBehavior { - public function getNewEmail() : string { + public function getNewEmail(): string { return $this->getKey('newEmail'); } - public function setNewEmail(string $newEmail) { + public function setNewEmail(string $newEmail): void { $this->setKey('newEmail', $newEmail); } diff --git a/composer.json b/composer.json index 2f89bdd..0b2d338 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,9 @@ "codeception/specify": "^1.0.0", "codeception/verify": "*", "mockery/mockery": "^1.0.0", - "php-mock/php-mock-mockery": "^1.2.0" + "php-mock/php-mock-mockery": "^1.2.0", + "friendsofphp/php-cs-fixer": "^2.11", + "ely/php-code-style": "^0.1.0" }, "repositories": [ { diff --git a/composer.lock b/composer.lock index d3642b6..19ec143 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "b322fafe1486ee057b633a35d336c1a1", + "content-hash": "63497214afa6b50f50d3bc80fe9eb269", "packages": [ { "name": "bacon/bacon-qr-code", @@ -3165,6 +3165,68 @@ "description": "BDD assertion library for PHPUnit", "time": "2017-11-12T01:51:59+00:00" }, + { + "name": "composer/semver", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573", + "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.5 || ^5.0.5", + "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "time": "2016-08-30T16:08:34+00:00" + }, { "name": "doctrine/instantiator", "version": "1.1.0", @@ -3219,6 +3281,55 @@ ], "time": "2017-07-22T11:58:36+00:00" }, + { + "name": "ely/php-code-style", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/elyby/php-code-style.git", + "reference": "0bb3e8082753981af4775f5b95d159c9cd9c6bf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/elyby/php-code-style/zipball/0bb3e8082753981af4775f5b95d159c9cd9c6bf4", + "reference": "0bb3e8082753981af4775f5b95d159c9cd9c6bf4", + "shasum": "" + }, + "require": { + "friendsofphp/php-cs-fixer": "^2.11", + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ely\\CS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Ely.by team", + "email": "team@ely.by" + }, + { + "name": "ErickSkrauch", + "email": "erickskrauch@ely.by" + } + ], + "description": "Set of PHP-CS-Fixer rules used in the development of Ely.by PHP projects", + "homepage": "https://github.com/elyby/php-code-style", + "keywords": [ + "Code style", + "php-cs-fixer" + ], + "time": "2018-04-17T18:28:51+00:00" + }, { "name": "facebook/webdriver", "version": "1.5.0", @@ -3315,6 +3426,100 @@ "description": "JSONPath implementation for parsing, searching and flattening arrays", "time": "2016-09-06T17:43:18+00:00" }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v2.11.1", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", + "reference": "ad94441c17b8ef096e517acccdbf3238af8a2da8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/ad94441c17b8ef096e517acccdbf3238af8a2da8", + "reference": "ad94441c17b8ef096e517acccdbf3238af8a2da8", + "shasum": "" + }, + "require": { + "composer/semver": "^1.4", + "doctrine/annotations": "^1.2", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^5.6 || >=7.0 <7.3", + "php-cs-fixer/diff": "^1.3", + "symfony/console": "^3.2 || ^4.0", + "symfony/event-dispatcher": "^3.0 || ^4.0", + "symfony/filesystem": "^3.0 || ^4.0", + "symfony/finder": "^3.0 || ^4.0", + "symfony/options-resolver": "^3.0 || ^4.0", + "symfony/polyfill-php70": "^1.0", + "symfony/polyfill-php72": "^1.4", + "symfony/process": "^3.0 || ^4.0", + "symfony/stopwatch": "^3.0 || ^4.0" + }, + "conflict": { + "hhvm": "*" + }, + "require-dev": { + "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", + "justinrainbow/json-schema": "^5.0", + "keradus/cli-executor": "^1.0", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.0", + "php-cs-fixer/accessible-object": "^1.0", + "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", + "phpunitgoodpractices/traits": "^1.3.1", + "symfony/phpunit-bridge": "^3.2.2 || ^4.0" + }, + "suggest": { + "ext-mbstring": "For handling non-UTF8 characters in cache signature.", + "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "extra": { + "branch-alias": { + "dev-master": "2.11-dev" + } + }, + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + }, + "classmap": [ + "tests/Test/AbstractFixerTestCase.php", + "tests/Test/AbstractIntegrationCaseFactory.php", + "tests/Test/AbstractIntegrationTestCase.php", + "tests/Test/Assert/AssertTokensTrait.php", + "tests/Test/Constraint/SameStringsConstraint.php", + "tests/Test/Constraint/SameStringsConstraintForV5.php", + "tests/Test/Constraint/SameStringsConstraintForV7.php", + "tests/Test/IntegrationCase.php", + "tests/Test/IntegrationCaseFactory.php", + "tests/Test/IntegrationCaseFactoryInterface.php", + "tests/Test/InternalIntegrationCaseFactory.php", + "tests/TestCase.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "time": "2018-03-21T17:41:26+00:00" + }, { "name": "fzaninotto/faker", "version": "v1.7.1", @@ -3625,6 +3830,57 @@ "description": "Library for handling version information and constraints", "time": "2017-03-05T17:38:23+00:00" }, + { + "name": "php-cs-fixer/diff", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/diff.git", + "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/78bb099e9c16361126c86ce82ec4405ebab8e756", + "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "symfony/process": "^3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "SpacePossum" + } + ], + "description": "sebastian/diff v2 backport support for PHP5.6", + "homepage": "https://github.com/PHP-CS-Fixer", + "keywords": [ + "diff" + ], + "time": "2018-02-15T16:58:55+00:00" + }, { "name": "php-mock/php-mock", "version": "1.0.1", @@ -5257,6 +5513,55 @@ "homepage": "https://symfony.com", "time": "2018-01-03T07:38:00+00:00" }, + { + "name": "symfony/filesystem", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21", + "reference": "5d2d655b2c72fc4d9bf7e9bf14f72a447b940f21", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2018-02-22T10:50:29+00:00" + }, { "name": "symfony/finder", "version": "v4.0.4", @@ -5306,6 +5611,164 @@ "homepage": "https://symfony.com", "time": "2018-01-03T07:38:00+00:00" }, + { + "name": "symfony/options-resolver", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "371532a2cfe932f7a3766dd4c45364566def1dd0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/371532a2cfe932f7a3766dd4c45364566def1dd0", + "reference": "371532a2cfe932f7a3766dd4c45364566def1dd0", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony OptionsResolver Component", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "time": "2018-01-18T22:19:33+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "8eca20c8a369e069d4f4c2ac9895144112867422" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/8eca20c8a369e069d4f4c2ac9895144112867422", + "reference": "8eca20c8a369e069d4f4c2ac9895144112867422", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2018-01-31T17:43:24+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "6795ffa2f8eebedac77f045aa62c0c10b2763042" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/6795ffa2f8eebedac77f045aa62c0c10b2763042", + "reference": "6795ffa2f8eebedac77f045aa62c0c10b2763042", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Stopwatch Component", + "homepage": "https://symfony.com", + "time": "2018-02-19T16:50:22+00:00" + }, { "name": "symfony/yaml", "version": "v4.0.4", diff --git a/console/controllers/AmqpController.php b/console/controllers/AmqpController.php index e06c765..1c6360c 100644 --- a/console/controllers/AmqpController.php +++ b/console/controllers/AmqpController.php @@ -17,7 +17,7 @@ abstract class AmqpController extends Controller { private $reconnected = false; - public final function actionIndex() { + final public function actionIndex() { $this->start(); } diff --git a/console/controllers/CleanupController.php b/console/controllers/CleanupController.php index fc72371..ef1bff0 100644 --- a/console/controllers/CleanupController.php +++ b/console/controllers/CleanupController.php @@ -85,7 +85,7 @@ class CleanupController extends Controller { $durationsMap = []; foreach (EmailActivation::getClassMap() as $typeId => $className) { /** @var EmailActivation $object */ - $object = new $className; + $object = new $className(); /** @var \common\behaviors\EmailActivationExpirationBehavior $behavior */ $behavior = $object->getBehavior('expirationBehavior'); /** @noinspection NullPointerExceptionInspection */ diff --git a/console/controllers/RbacController.php b/console/controllers/RbacController.php index 0c615da..66f9a7c 100644 --- a/console/controllers/RbacController.php +++ b/console/controllers/RbacController.php @@ -95,7 +95,7 @@ class RbacController extends Controller { $authManager = $this->getAuthManager(); $permission = $authManager->createPermission($name); if ($ruleClassName !== null) { - $rule = new $ruleClassName; + $rule = new $ruleClassName(); if (!$rule instanceof Rule) { throw new InvalidArgumentException('ruleClassName must be rule class name'); } diff --git a/console/db/Migration.php b/console/db/Migration.php index 9633643..e05b6aa 100644 --- a/console/db/Migration.php +++ b/console/db/Migration.php @@ -43,7 +43,7 @@ class Migration extends YiiMigration { private function buildKey(array $columns) { $key = ''; foreach ($columns as $i => $column) { - $key .= $i == count($columns) ? $column : "$column,"; + $key .= $i === count($columns) ? $column : "$column,"; } return $key; diff --git a/console/migrations/m160201_055928_oauth.php b/console/migrations/m160201_055928_oauth.php index bfc71c7..9347b3c 100644 --- a/console/migrations/m160201_055928_oauth.php +++ b/console/migrations/m160201_055928_oauth.php @@ -6,14 +6,14 @@ class m160201_055928_oauth extends Migration { public function safeUp() { $this->createTable('{{%oauth_clients}}', [ - 'id' => $this->string(64), - 'secret' => $this->string()->notNull(), - 'name' => $this->string()->notNull(), - 'description' => $this->string(), + 'id' => $this->string(64), + 'secret' => $this->string()->notNull(), + 'name' => $this->string()->notNull(), + 'description' => $this->string(), 'redirect_uri' => $this->string()->notNull(), - 'account_id' => $this->getDb()->getTableSchema('{{%accounts}}')->getColumn('id')->dbType, - 'is_trusted' => $this->boolean()->defaultValue(false)->notNull(), - 'created_at' => $this->integer()->notNull(), + 'account_id' => $this->getDb()->getTableSchema('{{%accounts}}')->getColumn('id')->dbType, + 'is_trusted' => $this->boolean()->defaultValue(false)->notNull(), + 'created_at' => $this->integer()->notNull(), $this->primary('id'), ], $this->tableOptions); @@ -23,17 +23,17 @@ class m160201_055928_oauth extends Migration { ], $this->tableOptions); $this->createTable('{{%oauth_sessions}}', [ - 'id' => $this->primaryKey(), - 'owner_type' => $this->string()->notNull(), - 'owner_id' => $this->string()->notNull(), - 'client_id' => $this->getDb()->getTableSchema('{{%oauth_clients}}')->getColumn('id')->dbType, + 'id' => $this->primaryKey(), + 'owner_type' => $this->string()->notNull(), + 'owner_id' => $this->string()->notNull(), + 'client_id' => $this->getDb()->getTableSchema('{{%oauth_clients}}')->getColumn('id')->dbType, 'client_redirect_uri' => $this->string(), ], $this->tableOptions); $this->createTable('{{%oauth_access_tokens}}', [ 'access_token' => $this->string(64), - 'session_id' => $this->getDb()->getTableSchema('{{%oauth_sessions}}')->getColumn('id')->dbType, - 'expire_time' => $this->integer()->notNull(), + 'session_id' => $this->getDb()->getTableSchema('{{%oauth_sessions}}')->getColumn('id')->dbType, + 'expire_time' => $this->integer()->notNull(), $this->primary('access_token'), ], $this->tableOptions); diff --git a/console/migrations/m160414_231110_account_nicknames_history.php b/console/migrations/m160414_231110_account_nicknames_history.php index 944ba3b..1598535 100644 --- a/console/migrations/m160414_231110_account_nicknames_history.php +++ b/console/migrations/m160414_231110_account_nicknames_history.php @@ -21,7 +21,7 @@ class m160414_231110_account_nicknames_history extends Migration { FROM {{%accounts}} ')->queryAll(); - foreach($accountNicknames as $row) { + foreach ($accountNicknames as $row) { $this->insert('{{%usernames_history}}', [ 'username' => $row['username'], 'account_id' => $row['id'], diff --git a/console/migrations/m160819_211139_minecraft_access_tokens.php b/console/migrations/m160819_211139_minecraft_access_tokens.php index 8756c47..95ceaf8 100644 --- a/console/migrations/m160819_211139_minecraft_access_tokens.php +++ b/console/migrations/m160819_211139_minecraft_access_tokens.php @@ -8,9 +8,9 @@ class m160819_211139_minecraft_access_tokens extends Migration { $this->createTable('{{%minecraft_access_keys}}', [ 'access_token' => $this->string(36)->notNull(), 'client_token' => $this->string(36)->notNull(), - 'account_id' => $this->db->getTableSchema('{{%accounts}}')->getColumn('id')->dbType . ' NOT NULL', - 'created_at' => $this->integer()->unsigned()->notNull(), - 'updated_at' => $this->integer()->unsigned()->notNull(), + 'account_id' => $this->db->getTableSchema('{{%accounts}}')->getColumn('id')->dbType . ' NOT NULL', + 'created_at' => $this->integer()->unsigned()->notNull(), + 'updated_at' => $this->integer()->unsigned()->notNull(), ]); $this->addPrimaryKey('access_token', '{{%minecraft_access_keys}}', 'access_token'); diff --git a/console/migrations/m161030_013122_ely_by_admin_app.php b/console/migrations/m161030_013122_ely_by_admin_app.php index 49fa91b..628e447 100644 --- a/console/migrations/m161030_013122_ely_by_admin_app.php +++ b/console/migrations/m161030_013122_ely_by_admin_app.php @@ -4,8 +4,6 @@ use console\db\Migration; class m161030_013122_ely_by_admin_app extends Migration { - const APP_NAME = 'ely_admin'; - public function safeUp() { $exists = $this->db->createCommand(' SELECT COUNT(*) @@ -13,12 +11,12 @@ class m161030_013122_ely_by_admin_app extends Migration { WHERE id = :app_name LIMIT 1 ', [ - 'app_name' => self::APP_NAME, + 'app_name' => 'ely_admin', ])->queryScalar(); if (!$exists) { $this->insert('{{%oauth_clients}}', [ - 'id' => self::APP_NAME, + 'id' => 'ely_admin', 'secret' => 'change_this_on_production', 'name' => 'Admin Ely.by', 'description' => '', @@ -30,7 +28,7 @@ class m161030_013122_ely_by_admin_app extends Migration { } public function safeDown() { - $this->delete('{{%oauth_clients}}', ['id' => self::APP_NAME]); + $this->delete('{{%oauth_clients}}', ['id' => 'ely_admin']); } } diff --git a/console/migrations/m161222_222520_remove_oauth_access_tokens.php b/console/migrations/m161222_222520_remove_oauth_access_tokens.php index ebc228d..945f659 100644 --- a/console/migrations/m161222_222520_remove_oauth_access_tokens.php +++ b/console/migrations/m161222_222520_remove_oauth_access_tokens.php @@ -12,8 +12,8 @@ class m161222_222520_remove_oauth_access_tokens extends Migration { public function safeDown() { $this->createTable('{{%oauth_access_tokens}}', [ 'access_token' => $this->string(64), - 'session_id' => $this->getDb()->getTableSchema('{{%oauth_sessions}}')->getColumn('id')->dbType, - 'expire_time' => $this->integer()->notNull(), + 'session_id' => $this->getDb()->getTableSchema('{{%oauth_sessions}}')->getColumn('id')->dbType, + 'expire_time' => $this->integer()->notNull(), $this->primary('access_token'), ], $this->tableOptions); diff --git a/tests/codeception/api/_bootstrap.php b/tests/codeception/api/_bootstrap.php index fe5358e..3bd1f3c 100644 --- a/tests/codeception/api/_bootstrap.php +++ b/tests/codeception/api/_bootstrap.php @@ -17,7 +17,7 @@ require_once __DIR__ . '/../../../api/config/bootstrap.php'; // the entry script file path for functional tests $_SERVER['SCRIPT_FILENAME'] = API_ENTRY_FILE; $_SERVER['SCRIPT_NAME'] = API_ENTRY_URL; -$_SERVER['SERVER_NAME'] = parse_url(Configuration::config()['config']['test_entry_url'], PHP_URL_HOST); -$_SERVER['SERVER_PORT'] = parse_url(Configuration::config()['config']['test_entry_url'], PHP_URL_PORT) ?: '80'; +$_SERVER['SERVER_NAME'] = parse_url(Configuration::config()['config']['test_entry_url'], PHP_URL_HOST); +$_SERVER['SERVER_PORT'] = parse_url(Configuration::config()['config']['test_entry_url'], PHP_URL_PORT) ?: '80'; Yii::setAlias('@tests', dirname(__DIR__, 2)); diff --git a/tests/codeception/api/_support/UnitTester.php b/tests/codeception/api/_support/UnitTester.php index 1919072..33d0a54 100644 --- a/tests/codeception/api/_support/UnitTester.php +++ b/tests/codeception/api/_support/UnitTester.php @@ -16,11 +16,11 @@ namespace tests\codeception\api; * * @SuppressWarnings(PHPMD) */ -class UnitTester extends \Codeception\Actor -{ +class UnitTester extends \Codeception\Actor { use _generated\UnitTesterActions; - /** - * Define custom actions here - */ + /** + * Define custom actions here + */ + } diff --git a/tests/codeception/api/functional/_steps/SessionServerSteps.php b/tests/codeception/api/functional/_steps/SessionServerSteps.php index a976752..8c97085 100644 --- a/tests/codeception/api/functional/_steps/SessionServerSteps.php +++ b/tests/codeception/api/functional/_steps/SessionServerSteps.php @@ -35,7 +35,7 @@ class SessionServerSteps extends FunctionalTester { return [$username, $serverId]; } - + public function canSeeValidTexturesResponse($expectedUsername, $expectedUuid) { $this->seeResponseIsJson(); $this->canSeeResponseContainsJson([ diff --git a/tests/codeception/api/functional/authserver/AuthorizationCest.php b/tests/codeception/api/functional/authserver/AuthorizationCest.php index 2b17c1e..8dd03c7 100644 --- a/tests/codeception/api/functional/authserver/AuthorizationCest.php +++ b/tests/codeception/api/functional/authserver/AuthorizationCest.php @@ -1,8 +1,8 @@ wantTo('get specific response when pass too many usernames'); $usernames = []; - for($i = 0; $i < 150; $i++) { + for ($i = 0; $i < 150; $i++) { $usernames[] = random_bytes(10); } + $this->route->uuidsByUsernames($usernames); $I->canSeeResponseCodeIs(400); $I->canSeeResponseIsJson(); diff --git a/tests/codeception/api/unit/TestCase.php b/tests/codeception/api/unit/TestCase.php index 91b79ad..f62c5b8 100644 --- a/tests/codeception/api/unit/TestCase.php +++ b/tests/codeception/api/unit/TestCase.php @@ -3,13 +3,18 @@ namespace tests\codeception\api\unit; use Mockery; -class TestCase extends \Codeception\Test\Unit { +class TestCase extends \Codeception\Test\Unit { /** * @var \tests\codeception\api\UnitTester */ protected $tester; + protected function tearDown() { + parent::tearDown(); + Mockery::close(); + } + /** * Список фикстур, что будут загружены перед тестом, но после зачистки базы данных * @@ -21,9 +26,4 @@ class TestCase extends \Codeception\Test\Unit { return []; } - protected function tearDown() { - parent::tearDown(); - Mockery::close(); - } - } diff --git a/tests/codeception/api/unit/components/User/ComponentTest.php b/tests/codeception/api/unit/components/User/ComponentTest.php index 65cd5f9..00f117b 100644 --- a/tests/codeception/api/unit/components/User/ComponentTest.php +++ b/tests/codeception/api/unit/components/User/ComponentTest.php @@ -1,9 +1,9 @@ specify('error.login_not_exist if login not exists', function () { + $this->specify('error.login_not_exist if login not exists', function() { $model = $this->createModel([ 'login' => 'mr-test', 'account' => null, @@ -41,7 +41,7 @@ class LoginFormTest extends TestCase { $this->assertEquals(['error.login_not_exist'], $model->getErrors('login')); }); - $this->specify('no errors if login exists', function () { + $this->specify('no errors if login exists', function() { $model = $this->createModel([ 'login' => 'mr-test', 'account' => new Account(), @@ -52,7 +52,7 @@ class LoginFormTest extends TestCase { } public function testValidatePassword() { - $this->specify('error.password_incorrect if password invalid', function () { + $this->specify('error.password_incorrect if password invalid', function() { $model = $this->createModel([ 'password' => '87654321', 'account' => new Account(['password' => '12345678']), @@ -61,7 +61,7 @@ class LoginFormTest extends TestCase { $this->assertEquals(['error.password_incorrect'], $model->getErrors('password')); }); - $this->specify('no errors if password valid', function () { + $this->specify('no errors if password valid', function() { $model = $this->createModel([ 'password' => '12345678', 'account' => new Account(['password' => '12345678']), @@ -100,7 +100,7 @@ class LoginFormTest extends TestCase { } public function testValidateActivity() { - $this->specify('error.account_not_activated if account in not activated state', function () { + $this->specify('error.account_not_activated if account in not activated state', function() { $model = $this->createModel([ 'account' => new Account(['status' => Account::STATUS_REGISTERED]), ]); @@ -108,7 +108,7 @@ class LoginFormTest extends TestCase { $this->assertEquals(['error.account_not_activated'], $model->getErrors('login')); }); - $this->specify('error.account_banned if account has banned status', function () { + $this->specify('error.account_banned if account has banned status', function() { $model = $this->createModel([ 'account' => new Account(['status' => Account::STATUS_BANNED]), ]); @@ -116,7 +116,7 @@ class LoginFormTest extends TestCase { $this->assertEquals(['error.account_banned'], $model->getErrors('login')); }); - $this->specify('no errors if account active', function () { + $this->specify('no errors if account active', function() { $model = $this->createModel([ 'account' => new Account(['status' => Account::STATUS_ACTIVE]), ]); diff --git a/tests/codeception/api/unit/models/authentication/LogoutFormTest.php b/tests/codeception/api/unit/models/authentication/LogoutFormTest.php index 861a666..7f63533 100644 --- a/tests/codeception/api/unit/models/authentication/LogoutFormTest.php +++ b/tests/codeception/api/unit/models/authentication/LogoutFormTest.php @@ -13,7 +13,7 @@ class LogoutFormTest extends TestCase { use Specify; public function testValidateLogout() { - $this->specify('No actions if active session is not exists', function () { + $this->specify('No actions if active session is not exists', function() { $userComp = $this ->getMockBuilder(Component::class) ->setConstructorArgs([$this->getComponentArgs()]) @@ -30,7 +30,7 @@ class LogoutFormTest extends TestCase { expect($model->logout())->true(); }); - $this->specify('if active session is presented, then delete should be called', function () { + $this->specify('if active session is presented, then delete should be called', function() { $session = $this ->getMockBuilder(AccountSession::class) ->setMethods(['delete']) diff --git a/tests/codeception/api/unit/modules/internal/models/BanFormTest.php b/tests/codeception/api/unit/modules/internal/models/BanFormTest.php index a9fe788..4cd3c02 100644 --- a/tests/codeception/api/unit/modules/internal/models/BanFormTest.php +++ b/tests/codeception/api/unit/modules/internal/models/BanFormTest.php @@ -1,8 +1,8 @@ shouldReceive('isClientExists')->andReturn(false); $request = new class implements OauthClientTypeForm { - public function load($data): bool { return true; } @@ -72,7 +71,6 @@ class OauthClientFormTest extends TestCase { $client->name = 'New name'; $client->description = 'New description.'; } - }; $this->assertTrue($form->save($request)); diff --git a/tests/codeception/api/unit/modules/session/filters/RateLimiterTest.php b/tests/codeception/api/unit/modules/session/filters/RateLimiterTest.php index a0c3710..81fc370 100644 --- a/tests/codeception/api/unit/modules/session/filters/RateLimiterTest.php +++ b/tests/codeception/api/unit/modules/session/filters/RateLimiterTest.php @@ -25,7 +25,7 @@ class RateLimiterTest extends TestCase { /** @var RateLimiter|\PHPUnit_Framework_MockObject_MockObject $filter */ $filter = $this->getMockBuilder(RateLimiter::class) ->setConstructorArgs([[ - 'authserverDomain' => Yii::$app->params['authserverHost'] + 'authserverDomain' => Yii::$app->params['authserverHost'], ]]) ->setMethods(['getServer']) ->getMock(); @@ -58,7 +58,7 @@ class RateLimiterTest extends TestCase { ->will($this->returnValue('http://authserver.ely.by')); $filter = new RateLimiter([ - 'authserverDomain' => Yii::$app->params['authserverHost'] + 'authserverDomain' => Yii::$app->params['authserverHost'], ]); $filter->checkRateLimit(null, $request, null, null); } diff --git a/tests/codeception/api/unit/validators/PasswordRequiredValidatorTest.php b/tests/codeception/api/unit/validators/PasswordRequiredValidatorTest.php index e6fdc38..b2b5aa5 100644 --- a/tests/codeception/api/unit/validators/PasswordRequiredValidatorTest.php +++ b/tests/codeception/api/unit/validators/PasswordRequiredValidatorTest.php @@ -2,11 +2,11 @@ namespace codeception\api\unit\validators; use api\validators\PasswordRequiredValidator; +use common\helpers\Error as E; use common\models\Account; use common\rbac\Permissions as P; use tests\codeception\api\unit\TestCase; use tests\codeception\common\_support\ProtectedCaller; -use common\helpers\Error as E; use yii\web\User; class PasswordRequiredValidatorTest extends TestCase { diff --git a/tests/codeception/common/_support/ProtectedCaller.php b/tests/codeception/common/_support/ProtectedCaller.php index e62b56b..ca28cf9 100644 --- a/tests/codeception/common/_support/ProtectedCaller.php +++ b/tests/codeception/common/_support/ProtectedCaller.php @@ -1,7 +1,6 @@ getYii2()->grabComponent($component); } - private function getYii2() : Yii2 { + private function getYii2(): Yii2 { $yii2 = $this->getModule('Yii2'); if (!$yii2 instanceof Yii2) { throw new ModuleException($this, 'Yii2 module must be configured'); diff --git a/tests/codeception/common/_support/amqp/TestComponent.php b/tests/codeception/common/_support/amqp/TestComponent.php index f305e7d..ed90730 100644 --- a/tests/codeception/common/_support/amqp/TestComponent.php +++ b/tests/codeception/common/_support/amqp/TestComponent.php @@ -40,13 +40,13 @@ class TestComponent extends Component { * @param string|null $exchangeName * @return \PhpAmqpLib\Message\AMQPMessage[] */ - public function getSentMessages(string $exchangeName = null) : array { + public function getSentMessages(string $exchangeName = null): array { if ($exchangeName !== null) { return $this->sentMessages[$exchangeName] ?? []; } $messages = []; - foreach($this->sentMessages as $exchangeGroup) { + foreach ($this->sentMessages as $exchangeGroup) { foreach ($exchangeGroup as $message) { $messages[] = $message; } diff --git a/tests/codeception/common/_support/queue/Queue.php b/tests/codeception/common/_support/queue/Queue.php index 169da26..e6b57d6 100644 --- a/tests/codeception/common/_support/queue/Queue.php +++ b/tests/codeception/common/_support/queue/Queue.php @@ -8,6 +8,11 @@ class Queue extends BaseQueue { private $messages = []; + public function __set($name, $value) { + // Yii2 components may contains some configuration + // But we just ignore it for this mock component + } + public function push($job) { $this->messages[] = $job; } @@ -24,9 +29,4 @@ class Queue extends BaseQueue { // This function is abstract, but will be not called } - public function __set($name, $value) { - // Yii2 components may contains some configuration - // But we just ignore it for this mock component - } - } diff --git a/tests/codeception/common/unit/TestCase.php b/tests/codeception/common/unit/TestCase.php index b3c1f9b..2a72963 100644 --- a/tests/codeception/common/unit/TestCase.php +++ b/tests/codeception/common/unit/TestCase.php @@ -3,13 +3,18 @@ namespace tests\codeception\common\unit; use Mockery; -class TestCase extends \Codeception\Test\Unit { +class TestCase extends \Codeception\Test\Unit { /** * @var \tests\codeception\common\UnitTester */ protected $tester; + protected function tearDown() { + parent::tearDown(); + Mockery::close(); + } + /** * Список фикстур, что будут загружены перед тестом, но после зачистки базы данных * @@ -21,9 +26,4 @@ class TestCase extends \Codeception\Test\Unit { return []; } - protected function tearDown() { - parent::tearDown(); - Mockery::close(); - } - } diff --git a/tests/codeception/common/unit/behaviors/PrimaryKeyValueBehaviorTest.php b/tests/codeception/common/unit/behaviors/PrimaryKeyValueBehaviorTest.php index a7b9cc1..4ec7406 100644 --- a/tests/codeception/common/unit/behaviors/PrimaryKeyValueBehaviorTest.php +++ b/tests/codeception/common/unit/behaviors/PrimaryKeyValueBehaviorTest.php @@ -14,17 +14,17 @@ class PrimaryKeyValueBehaviorTest extends TestCase { $model = new DummyModel(); /** @var PrimaryKeyValueBehavior|\PHPUnit_Framework_MockObject_MockObject $behavior */ $behavior = $this->getMockBuilder(PrimaryKeyValueBehavior::class) - ->setMethods(['isValueExists']) - ->setConstructorArgs([[ - 'value' => function() { - return 'mock'; - }, - ]]) - ->getMock(); + ->setMethods(['isValueExists']) + ->setConstructorArgs([[ + 'value' => function() { + return 'mock'; + }, + ]]) + ->getMock(); $behavior->expects($this->once()) - ->method('isValueExists') - ->will($this->returnValue(false)); + ->method('isValueExists') + ->will($this->returnValue(false)); $model->attachBehavior('primary-key-value-behavior', $behavior); $behavior->setPrimaryKeyValue(); @@ -44,12 +44,12 @@ class PrimaryKeyValueBehaviorTest extends TestCase { ->getMock(); $behavior->expects($this->exactly(3)) - ->method('generateValue') - ->will($this->onConsecutiveCalls('1', '2', '3')); + ->method('generateValue') + ->will($this->onConsecutiveCalls('1', '2', '3')); $behavior->expects($this->exactly(3)) - ->method('isValueExists') - ->will($this->onConsecutiveCalls(true, true, false)); + ->method('isValueExists') + ->will($this->onConsecutiveCalls(true, true, false)); $model->attachBehavior('primary-key-value-behavior', $behavior); $behavior->setPrimaryKeyValue(); diff --git a/tests/codeception/common/unit/validators/EmailValidatorTest.php b/tests/codeception/common/unit/validators/EmailValidatorTest.php index 614085a..caa24ce 100644 --- a/tests/codeception/common/unit/validators/EmailValidatorTest.php +++ b/tests/codeception/common/unit/validators/EmailValidatorTest.php @@ -115,7 +115,7 @@ class EmailValidatorTest extends TestCase { * @param string $fieldValue * @return Model */ - private function createModel(string $fieldValue) : Model { + private function createModel(string $fieldValue): Model { $class = new class extends Model { public $field; }; diff --git a/tests/codeception/common/unit/validators/UsernameValidatorTest.php b/tests/codeception/common/unit/validators/UsernameValidatorTest.php index 58154a5..b8bae8d 100644 --- a/tests/codeception/common/unit/validators/UsernameValidatorTest.php +++ b/tests/codeception/common/unit/validators/UsernameValidatorTest.php @@ -50,21 +50,22 @@ class UsernameValidatorTest extends TestCase { $this->assertNotEquals(['error.username_too_long'], $model->getErrors('field')); } + // TODO: rewrite this test with @provider usage public function testValidateAttributePattern() { $shouldBeValid = [ 'русский_ник', 'русский_ник_на_грани!', 'numbers1132', '*__*-Stars-*__*', '1-_.!$%^&*()[]', '[ESP]Эрик', 'Свят_помидор;', 'зроблена_ў_беларусі:)', ]; - foreach($shouldBeValid as $nickname) { + foreach ($shouldBeValid as $nickname) { $model = $this->createModel($nickname); $this->validator->validateAttribute($model, 'field'); $this->assertNotEquals(['error.username_invalid'], $model->getErrors('field')); } $shouldBeInvalid = [ - 'nick@name', 'spaced nick', 'im#hashed', 'quest?ion' + 'nick@name', 'spaced nick', 'im#hashed', 'quest?ion', ]; - foreach($shouldBeInvalid as $nickname) { + foreach ($shouldBeInvalid as $nickname) { $model = $this->createModel($nickname); $this->validator->validateAttribute($model, 'field'); $this->assertEquals(['error.username_invalid'], $model->getErrors('field')); @@ -100,7 +101,7 @@ class UsernameValidatorTest extends TestCase { * @param string $fieldValue * @return Model */ - private function createModel(string $fieldValue) : Model { + private function createModel(string $fieldValue): Model { $class = new class extends Model { public $field; }; diff --git a/tests/codeception/console/_support/UnitTester.php b/tests/codeception/console/_support/UnitTester.php index 3cf7a9c..a906302 100644 --- a/tests/codeception/console/_support/UnitTester.php +++ b/tests/codeception/console/_support/UnitTester.php @@ -16,11 +16,11 @@ namespace tests\codeception\console; * * @SuppressWarnings(PHPMD) */ -class UnitTester extends \Codeception\Actor -{ +class UnitTester extends \Codeception\Actor { use _generated\UnitTesterActions; - /** - * Define custom actions here - */ + /** + * Define custom actions here + */ + } diff --git a/tests/codeception/console/unit/TestCase.php b/tests/codeception/console/unit/TestCase.php index 650812b..d1d8a15 100644 --- a/tests/codeception/console/unit/TestCase.php +++ b/tests/codeception/console/unit/TestCase.php @@ -11,6 +11,11 @@ class TestCase extends Unit { */ protected $tester; + protected function tearDown() { + parent::tearDown(); + Mockery::close(); + } + /** * Список фикстур, что будут загружены перед тестом, но после зачистки базы данных * @@ -22,9 +27,4 @@ class TestCase extends Unit { return []; } - protected function tearDown() { - parent::tearDown(); - Mockery::close(); - } - } diff --git a/tests/codeception/console/unit/controllers/AccountQueueControllerTest.php b/tests/codeception/console/unit/controllers/AccountQueueControllerTest.php index 4c9e236..0fbe3fe 100644 --- a/tests/codeception/console/unit/controllers/AccountQueueControllerTest.php +++ b/tests/codeception/console/unit/controllers/AccountQueueControllerTest.php @@ -49,9 +49,9 @@ class AccountQueueControllerTest extends TestCase { ->willReturnCallback(function() { if ($this->expectedResponse === false) { throw new NoContentException(); - } else { - return $this->expectedResponse; } + + return $this->expectedResponse; }); $controller diff --git a/yii b/yii index 36011f7..20610d6 100755 --- a/yii +++ b/yii @@ -1,13 +1,13 @@ #!/usr/bin/env php Date: Wed, 18 Apr 2018 02:48:27 +0300 Subject: [PATCH 04/19] Simplify the call of the php-cs-fixer --- .gitlab-ci.yml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d24b075..bc8b26b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,19 +24,14 @@ test:backend: script: - export TEMP_DEV_IMAGE="${CONTAINER_IMAGE}:ci-${CI_BUILD_ID}" - docker build --pull -f Dockerfile-dev -t $TEMP_DEV_IMAGE . - # https://github.com/FriendsOfPHP/PHP-CS-Fixer#using-php-cs-fixer-on-ci - - COMMIT_RANGE="${CI_COMMIT_BEFORE_SHA}...${CI_COMMIT_SHA}" - - CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRTUXB "${COMMIT_RANGE}") - - | - if ! echo "${CHANGED_FILES}" | grep -qE "^(\\.php_cs(\\.dist)?|composer\\.lock)$"; then - EXTRA_ARGS=$(printf -- '--path-mode=intersection\n--\n%s' "${CHANGED_FILES}") - else - EXTRA_ARGS='' - fi - > docker run --rm $TEMP_DEV_IMAGE - vendor/bin/php-cs-fixer fix -v --dry-run --stop-on-violation --using-cache=no ${EXTRA_ARGS} + bash -c " + rm /usr/local/etc/php/conf.d/xdebug.ini && + cp -r /var/www/vendor /var/www/html/vendor && + vendor/bin/php-cs-fixer fix -v --dry-run + " - > docker run --rm --add-host=mariadb:`getent hosts mariadb | awk '{ print $1 ; exit }'` From bcff944be2e2d54becc9fda0acc58e7ca30e5205 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Wed, 18 Apr 2018 03:19:03 +0300 Subject: [PATCH 05/19] Disable xdebug for tests 'cause we don't collect coverage stats --- .gitlab-ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bc8b26b..8deb81b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -44,7 +44,10 @@ test:backend: -e DB_PASSWORD="ely_accounts_tester_password" -e REDIS_HOST="redis" $TEMP_DEV_IMAGE - php vendor/bin/codecept run -c tests + bash -c " + rm /usr/local/etc/php/conf.d/xdebug.ini && + docker-entrypoint.sh php vendor/bin/codecept run -c tests + " test:frontend: image: node:9.2.1-alpine From 03bd5ec1446ffaafd0b05523f465051d60ccac16 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sat, 21 Apr 2018 16:07:09 +0300 Subject: [PATCH 06/19] #398: implement idna domain conversion for emails --- common/validators/EmailValidator.php | 9 +++++++-- .../common/unit/validators/EmailValidatorTest.php | 12 ++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/common/validators/EmailValidator.php b/common/validators/EmailValidator.php index 1a46fe9..e774728 100644 --- a/common/validators/EmailValidator.php +++ b/common/validators/EmailValidator.php @@ -38,6 +38,11 @@ class EmailValidator extends Validator { $tempmail = new TempmailValidator(); $tempmail->message = E::EMAIL_IS_TEMPMAIL; + $idnaDomain = new validators\FilterValidator(['filter' => function(string $value): string { + [$name, $domain] = explode('@', $value); + return $name . '@' . idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46); + }]); + $unique = new validators\UniqueValidator(); $unique->message = E::EMAIL_NOT_AVAILABLE; $unique->targetClass = Account::class; @@ -53,12 +58,12 @@ class EmailValidator extends Validator { $this->executeValidation($length, $model, $attribute) && $this->executeValidation($email, $model, $attribute) && $this->executeValidation($tempmail, $model, $attribute) && + $this->executeValidation($idnaDomain, $model, $attribute) && $this->executeValidation($unique, $model, $attribute); } - protected function executeValidation(Validator $validator, Model $model, string $attribute) { + private function executeValidation(Validator $validator, Model $model, string $attribute): bool { $validator->validateAttribute($model, $attribute); - return !$model->hasErrors($attribute); } diff --git a/tests/codeception/common/unit/validators/EmailValidatorTest.php b/tests/codeception/common/unit/validators/EmailValidatorTest.php index caa24ce..7d33692 100644 --- a/tests/codeception/common/unit/validators/EmailValidatorTest.php +++ b/tests/codeception/common/unit/validators/EmailValidatorTest.php @@ -84,6 +84,18 @@ class EmailValidatorTest extends TestCase { $this->assertNotEquals(['error.email_is_tempmail'], $model->getErrors('field')); } + public function testValidateAttributeIdna() { + Mock::func(YiiEmailValidator::class, 'checkdnsrr')->times(2)->andReturnTrue(); + + $model = $this->createModel('qdushyantasunassm@❕.gq'); + $this->validator->validateAttribute($model, 'field'); + $this->assertSame('qdushyantasunassm@xn--bei.gq', $model->field); + + $model = $this->createModel('valid-email@gmail.com'); + $this->validator->validateAttribute($model, 'field'); + $this->assertSame('valid-email@gmail.com', $model->field); + } + public function testValidateAttributeUnique() { Mock::func(YiiEmailValidator::class, 'checkdnsrr')->times(3)->andReturnTrue(); From 6751eb659192e20dca10855fd5bd9226663bea34 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sat, 7 Jul 2018 15:01:18 +0300 Subject: [PATCH 07/19] Implemented webhooks database structure and console command register webhooks --- common/models/WebHook.php | 42 +++++++++++ common/models/WebHookEvent.php | 27 +++++++ console/controllers/WebhooksController.php | 57 +++++++++++++++ console/db/Migration.php | 24 ++----- .../migrations/m180706_230451_webhooks.php | 28 ++++++++ console/models/.gitkeep | 1 - console/models/WebHookForm.php | 70 +++++++++++++++++++ 7 files changed, 228 insertions(+), 21 deletions(-) create mode 100644 common/models/WebHook.php create mode 100644 common/models/WebHookEvent.php create mode 100644 console/controllers/WebhooksController.php create mode 100644 console/migrations/m180706_230451_webhooks.php delete mode 100644 console/models/.gitkeep create mode 100644 console/models/WebHookForm.php diff --git a/common/models/WebHook.php b/common/models/WebHook.php new file mode 100644 index 0000000..586fa9e --- /dev/null +++ b/common/models/WebHook.php @@ -0,0 +1,42 @@ + TimestampBehavior::class, + 'updatedAtAttribute' => false, + ], + ]; + } + + public function getEvents(): ActiveQueryInterface { + return $this->hasMany(WebHookEvent::class, ['webhook_id' => 'id']); + } + +} diff --git a/common/models/WebHookEvent.php b/common/models/WebHookEvent.php new file mode 100644 index 0000000..280d4ab --- /dev/null +++ b/common/models/WebHookEvent.php @@ -0,0 +1,27 @@ +hasOne(WebHook::class, ['id' => 'webhook_id']); + } + +} diff --git a/console/controllers/WebhooksController.php b/console/controllers/WebhooksController.php new file mode 100644 index 0000000..6f77acc --- /dev/null +++ b/console/controllers/WebhooksController.php @@ -0,0 +1,57 @@ + true, + 'validator' => function(string $input, ?string &$error) use ($form): bool { + $form->url = $input; + if (!$form->validate('url')) { + $error = $form->getFirstError('url'); + return false; + } + + return true; + }, + ]); + $secret = Console::prompt('Enter webhook secret (empty to no secret):'); + + $options = $form::getEvents(); + $options[''] = 'Finish input'; // It's needed to allow finish input cycle + $events = []; + + do { + $availableOptions = array_diff($options, $events); + $eventIndex = Console::select('Choose wanted events (submit no input to finish):', $availableOptions); + if ($eventIndex !== '') { + $events[] = $options[$eventIndex]; + } + } while($eventIndex !== '' || empty($events)); // User must choose at least one event + + $form->url = $url; + $form->events = $events; + if ($secret !== '') { + $form->secret = $secret; + } + + if (!$form->save()) { + Console::error('Unable to create new webhook. Check errors list below' . PHP_EOL . Console::errorSummary($form)); + return ExitCode::UNSPECIFIED_ERROR; + } + + return ExitCode::OK; + } + +} diff --git a/console/db/Migration.php b/console/db/Migration.php index e05b6aa..3ec8284 100644 --- a/console/db/Migration.php +++ b/console/db/Migration.php @@ -25,28 +25,12 @@ class Migration extends YiiMigration { parent::createTable($table, $columns, $options); } - protected function primary(...$columns) { - switch (count($columns)) { - case 0: - $key = ''; - break; - case 1: - $key = $columns[0]; - break; - default: - $key = $this->buildKey($columns); + protected function primary(string ...$columns): string { + foreach ($columns as &$column) { + $column = $this->db->quoteColumnName($column); } - return " PRIMARY KEY ($key) "; - } - - private function buildKey(array $columns) { - $key = ''; - foreach ($columns as $i => $column) { - $key .= $i === count($columns) ? $column : "$column,"; - } - - return $key; + return ' PRIMARY KEY (' . implode(', ', $columns) . ') '; } } diff --git a/console/migrations/m180706_230451_webhooks.php b/console/migrations/m180706_230451_webhooks.php new file mode 100644 index 0000000..df6a620 --- /dev/null +++ b/console/migrations/m180706_230451_webhooks.php @@ -0,0 +1,28 @@ +createTable('{{%webhooks}}', [ + 'id' => $this->primaryKey(11)->unsigned(), + 'url' => $this->string()->notNull(), + 'secret' => $this->string(), + 'created_at' => $this->integer(11)->unsigned()->notNull(), + ]); + + $this->createTable('{{%webhooks_events}}', [ + 'webhook_id' => $this->db->getTableSchema('{{%webhooks}}')->getColumn('id')->dbType . ' NOT NULL', + 'event_type' => $this->string()->notNull(), + $this->primary('webhook_id', 'event_type'), + ]); + $this->addForeignKey('FK_webhook_event_to_webhook', '{{%webhooks_events}}', 'webhook_id', 'webhooks', 'id', 'CASCADE', 'CASCADE'); + } + + public function safeDown() { + $this->dropTable('{{%webhooks_events}}'); + $this->dropTable('{{%webhooks}}'); + } + +} diff --git a/console/models/.gitkeep b/console/models/.gitkeep deleted file mode 100644 index 72e8ffc..0000000 --- a/console/models/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/console/models/WebHookForm.php b/console/models/WebHookForm.php new file mode 100644 index 0000000..4f9cfcd --- /dev/null +++ b/console/models/WebHookForm.php @@ -0,0 +1,70 @@ +webHook = $webHook; + } + + public function rules(): array { + return [ + [['url'], 'required'], + [['url'], 'url'], + [['secret'], 'string'], + [['events'], 'in', 'range' => static::getEvents(), 'allowArray' => true], + ]; + } + + public function save(): bool { + if (!$this->validate()) { + return false; + } + + $transaction = Yii::$app->db->beginTransaction(); + + $webHook = $this->webHook; + $webHook->url = $this->url; + $webHook->secret = $this->secret; + if (!$webHook->save()) { + throw new ThisShouldNotHappenException('Cannot save webhook.'); + } + + foreach ($this->events as $event) { + $eventModel = new WebHookEvent(); + $eventModel->webhook_id = $webHook->id; + $eventModel->event_type = $event; + if (!$eventModel->save()) { + throw new ThisShouldNotHappenException('Cannot save webhook event.'); + } + } + + $transaction->commit(); + + return true; + } + + public static function getEvents(): array { + return [ + 'account.edit', + ]; + } + +} From c0aa78d1561fbd24b7cba7b270f5f643af86d2aa Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sun, 8 Jul 2018 18:20:19 +0300 Subject: [PATCH 08/19] Implemented WebHooks delivery queue. Completely removed usage of the RabbitMQ. Queue now based on Redis channels. Worker process now extracted as separate docker container. Base image upgraded to the 1.8.0 version (PHP 7.2.7 and pcntl extension). --- .env-dist | 12 - Dockerfile | 2 +- Dockerfile-dev | 2 +- .../authentication/ConfirmEmailForm.php | 6 +- .../accounts/models/BanAccountForm.php | 25 +- .../accounts/models/ChangeEmailForm.php | 23 +- .../accounts/models/ChangeUsernameForm.php | 32 +- .../accounts/models/PardonAccountForm.php | 20 +- api/modules/session/filters/RateLimiter.php | 12 +- api/modules/session/models/SessionModel.php | 6 +- autocompletion.php | 19 +- common/components/RabbitMQ/Component.php | 177 -------- common/components/RabbitMQ/Helper.php | 26 -- common/components/Redis/Cache.php | 13 - common/components/Redis/Connection.php | 415 ----------------- .../components/Redis/ConnectionInterface.php | 19 - common/components/Redis/Key.php | 20 +- common/components/Redis/Set.php | 13 +- common/config/config.php | 23 +- common/models/Account.php | 21 + common/tasks/ClearAccountSessions.php | 64 +++ common/tasks/CreateWebHooksDeliveries.php | 76 ++++ common/tasks/DeliveryWebHook.php | 110 +++++ common/tasks/PullMojangUsername.php | 72 +++ composer.json | 13 +- composer.lock | 426 +++--------------- .../controllers/AccountQueueController.php | 98 ---- console/controllers/AmqpController.php | 72 --- docker-compose.dev.yml | 23 +- docker-compose.prod.yml | 18 +- docker/phpmyadmin/Dockerfile | 2 +- .../supervisor/.gitkeep | 0 docker/supervisor/account-queue-worker.conf | 6 - docker/supervisor/worker-queue.conf | 6 - tests/codeception/api/functional.suite.yml | 1 - tests/codeception/api/unit.suite.yml | 1 - .../authentication/ConfirmEmailFormTest.php | 6 - .../accounts/models/ChangeEmailFormTest.php | 13 - .../models/ChangeUsernameFormTest.php | 23 +- .../modules/internal/models/BanFormTest.php | 27 +- .../internal/models/PardonFormTest.php | 11 - .../common/_support/amqp/Helper.php | 91 ---- .../common/_support/amqp/TestComponent.php | 58 --- .../_support/queue/CodeceptionQueueHelper.php | 7 +- .../common/fixtures/WebHooksEventsFixture.php | 19 + .../common/fixtures/WebHooksFixture.php | 15 + .../common/fixtures/data/webhooks-events.php | 11 + .../common/fixtures/data/webhooks.php | 21 + tests/codeception/common/unit.suite.yml | 1 + .../common/unit/models/AccountTest.php | 39 ++ .../unit/tasks/ClearAccountSessionsTest.php | 44 ++ .../tasks/CreateWebHooksDeliveriesTest.php | 91 ++++ .../common/unit/tasks/DeliveryWebHookTest.php | 132 ++++++ .../unit/tasks/PullMojangUsernameTest.php} | 131 +++--- tests/codeception/config/config.php | 3 - 55 files changed, 933 insertions(+), 1684 deletions(-) delete mode 100644 common/components/RabbitMQ/Component.php delete mode 100644 common/components/RabbitMQ/Helper.php delete mode 100644 common/components/Redis/Cache.php delete mode 100644 common/components/Redis/Connection.php delete mode 100644 common/components/Redis/ConnectionInterface.php create mode 100644 common/tasks/ClearAccountSessions.php create mode 100644 common/tasks/CreateWebHooksDeliveries.php create mode 100644 common/tasks/DeliveryWebHook.php create mode 100644 common/tasks/PullMojangUsername.php delete mode 100644 console/controllers/AccountQueueController.php delete mode 100644 console/controllers/AmqpController.php rename api/models/profile/TwoFactorAuthForm.php => docker/supervisor/.gitkeep (100%) delete mode 100644 docker/supervisor/account-queue-worker.conf delete mode 100644 docker/supervisor/worker-queue.conf delete mode 100644 tests/codeception/common/_support/amqp/Helper.php delete mode 100644 tests/codeception/common/_support/amqp/TestComponent.php create mode 100644 tests/codeception/common/fixtures/WebHooksEventsFixture.php create mode 100644 tests/codeception/common/fixtures/WebHooksFixture.php create mode 100644 tests/codeception/common/fixtures/data/webhooks-events.php create mode 100644 tests/codeception/common/fixtures/data/webhooks.php create mode 100644 tests/codeception/common/unit/tasks/ClearAccountSessionsTest.php create mode 100644 tests/codeception/common/unit/tasks/CreateWebHooksDeliveriesTest.php create mode 100644 tests/codeception/common/unit/tasks/DeliveryWebHookTest.php rename tests/codeception/{console/unit/controllers/AccountQueueControllerTest.php => common/unit/tasks/PullMojangUsernameTest.php} (50%) diff --git a/.env-dist b/.env-dist index a4fcd1c..8357fea 100644 --- a/.env-dist +++ b/.env-dist @@ -29,13 +29,6 @@ REDIS_PORT=6379 REDIS_DATABASE=0 REDIS_PASSWORD= -## Параметры подключения к rabbitmq -RABBITMQ_HOST=rabbitmq -RABBITMQ_PORT=5672 -RABBITMQ_USER=ely-accounts-app -RABBITMQ_PASS=ely-accounts-app-password -RABBITMQ_VHOST=/ely.by - ## Параметры Statsd STATSD_HOST=statsd.ely.by STATSD_PORT=8125 @@ -59,8 +52,3 @@ MYSQL_ROOT_PASSWORD= MYSQL_DATABASE=ely_accounts MYSQL_USER=ely_accounts_user MYSQL_PASSWORD=ely_accounts_password - -# RabbitMQ -RABBITMQ_DEFAULT_USER=ely-accounts-app -RABBITMQ_DEFAULT_PASS=ely-accounts-app-password -RABBITMQ_DEFAULT_VHOST=/ely.by diff --git a/Dockerfile b/Dockerfile index a6f7285..54d8000 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.ely.by/elyby/accounts-php:1.7.0 +FROM registry.ely.by/elyby/accounts-php:1.8.0 # bootstrap скрипт для проекта COPY docker/php/bootstrap.sh /bootstrap.sh diff --git a/Dockerfile-dev b/Dockerfile-dev index 07ec96a..dec0312 100644 --- a/Dockerfile-dev +++ b/Dockerfile-dev @@ -1,4 +1,4 @@ -FROM registry.ely.by/elyby/accounts-php:1.7.0-dev +FROM registry.ely.by/elyby/accounts-php:1.8.0-dev # bootstrap скрипт для проекта COPY docker/php/bootstrap.sh /bootstrap.sh diff --git a/api/models/authentication/ConfirmEmailForm.php b/api/models/authentication/ConfirmEmailForm.php index c813ac0..5d6b7cc 100644 --- a/api/models/authentication/ConfirmEmailForm.php +++ b/api/models/authentication/ConfirmEmailForm.php @@ -1,9 +1,10 @@ createEventTask($account->id, $account->username, null); - $transaction->commit(); return Yii::$app->user->createJwtAuthenticationToken($account, true); diff --git a/api/modules/accounts/models/BanAccountForm.php b/api/modules/accounts/models/BanAccountForm.php index 35f0d30..d35fec4 100644 --- a/api/modules/accounts/models/BanAccountForm.php +++ b/api/modules/accounts/models/BanAccountForm.php @@ -1,13 +1,11 @@ getAccount()->status === Account::STATUS_BANNED) { $this->addError('account', E::ACCOUNT_ALREADY_BANNED); } @@ -54,27 +52,14 @@ class BanAccountForm extends AccountActionForm { $account = $this->getAccount(); $account->status = Account::STATUS_BANNED; if (!$account->save()) { - throw new ErrorException('Cannot ban account'); + throw new ThisShouldNotHappenException('Cannot ban account'); } - $this->createTask(); + Yii::$app->queue->push(ClearAccountSessions::createFromAccount($account)); $transaction->commit(); return true; } - public function createTask(): void { - $model = new AccountBanned(); - $model->accountId = $this->getAccount()->id; - $model->duration = $this->duration; - $model->message = $this->message; - - $message = Amqp::getInstance()->prepareMessage($model, [ - 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, - ]); - - Amqp::sendToEventsExchange('accounts.account-banned', $message); - } - } diff --git a/api/modules/accounts/models/ChangeEmailForm.php b/api/modules/accounts/models/ChangeEmailForm.php index 1f03b67..3de16c6 100644 --- a/api/modules/accounts/models/ChangeEmailForm.php +++ b/api/modules/accounts/models/ChangeEmailForm.php @@ -2,13 +2,10 @@ namespace api\modules\accounts\models; use api\aop\annotations\CollectModelMetrics; +use api\exceptions\ThisShouldNotHappenException; use api\validators\EmailActivationKeyValidator; -use common\helpers\Amqp; -use common\models\amqp\EmailChanged; use common\models\EmailActivation; -use PhpAmqpLib\Message\AMQPMessage; use Yii; -use yii\base\ErrorException; class ChangeEmailForm extends AccountActionForm { @@ -35,30 +32,14 @@ class ChangeEmailForm extends AccountActionForm { $activation->delete(); $account = $this->getAccount(); - $oldEmail = $account->email; $account->email = $activation->newEmail; if (!$account->save()) { - throw new ErrorException('Cannot save new account email value'); + throw new ThisShouldNotHappenException('Cannot save new account email value'); } - $this->createTask($account->id, $account->email, $oldEmail); - $transaction->commit(); return true; } - public function createTask(int $accountId, string $newEmail, string $oldEmail): void { - $model = new EmailChanged(); - $model->accountId = $accountId; - $model->oldEmail = $oldEmail; - $model->newEmail = $newEmail; - - $message = Amqp::getInstance()->prepareMessage($model, [ - 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, - ]); - - Amqp::sendToEventsExchange('accounts.email-changed', $message); - } - } diff --git a/api/modules/accounts/models/ChangeUsernameForm.php b/api/modules/accounts/models/ChangeUsernameForm.php index 86fa961..53ad10d 100644 --- a/api/modules/accounts/models/ChangeUsernameForm.php +++ b/api/modules/accounts/models/ChangeUsernameForm.php @@ -4,13 +4,10 @@ namespace api\modules\accounts\models; use api\aop\annotations\CollectModelMetrics; use api\exceptions\ThisShouldNotHappenException; use api\validators\PasswordRequiredValidator; -use common\helpers\Amqp; -use common\models\amqp\UsernameChanged; use common\models\UsernameHistory; +use common\tasks\PullMojangUsername; use common\validators\UsernameValidator; -use PhpAmqpLib\Message\AMQPMessage; use Yii; -use yii\base\ErrorException; class ChangeUsernameForm extends AccountActionForm { @@ -42,7 +39,6 @@ class ChangeUsernameForm extends AccountActionForm { $transaction = Yii::$app->db->beginTransaction(); - $oldNickname = $account->username; $account->username = $this->username; if (!$account->save()) { throw new ThisShouldNotHappenException('Cannot save account model with new username'); @@ -52,36 +48,14 @@ class ChangeUsernameForm extends AccountActionForm { $usernamesHistory->account_id = $account->id; $usernamesHistory->username = $account->username; if (!$usernamesHistory->save()) { - throw new ErrorException('Cannot save username history record'); + throw new ThisShouldNotHappenException('Cannot save username history record'); } - $this->createEventTask($account->id, $account->username, $oldNickname); + Yii::$app->queue->push(PullMojangUsername::createFromAccount($account)); $transaction->commit(); return true; } - /** - * TODO: вынести это в отдельную сущность, т.к. эта команда используется внутри формы регистрации - * - * @param integer $accountId - * @param string $newNickname - * @param string $oldNickname - * - * @throws \PhpAmqpLib\Exception\AMQPExceptionInterface|\yii\base\Exception - */ - public function createEventTask($accountId, $newNickname, $oldNickname): void { - $model = new UsernameChanged(); - $model->accountId = $accountId; - $model->oldUsername = $oldNickname; - $model->newUsername = $newNickname; - - $message = Amqp::getInstance()->prepareMessage($model, [ - 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, - ]); - - Amqp::sendToEventsExchange('accounts.username-changed', $message); - } - } diff --git a/api/modules/accounts/models/PardonAccountForm.php b/api/modules/accounts/models/PardonAccountForm.php index 2c337d7..fe9c073 100644 --- a/api/modules/accounts/models/PardonAccountForm.php +++ b/api/modules/accounts/models/PardonAccountForm.php @@ -1,13 +1,10 @@ getAccount(); $account->status = Account::STATUS_ACTIVE; if (!$account->save()) { - throw new ErrorException('Cannot pardon account'); + throw new ThisShouldNotHappenException('Cannot pardon account'); } - $this->createTask(); - $transaction->commit(); return true; } - public function createTask(): void { - $model = new AccountPardoned(); - $model->accountId = $this->getAccount()->id; - - $message = Amqp::getInstance()->prepareMessage($model, [ - 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, - ]); - - Amqp::sendToEventsExchange('accounts.account-pardoned', $message); - } - } diff --git a/api/modules/session/filters/RateLimiter.php b/api/modules/session/filters/RateLimiter.php index 72081bc..96be790 100644 --- a/api/modules/session/filters/RateLimiter.php +++ b/api/modules/session/filters/RateLimiter.php @@ -56,10 +56,9 @@ class RateLimiter extends \yii\filters\RateLimiter { $ip = $request->getUserIP(); $key = $this->buildKey($ip); - $redis = $this->getRedis(); - $countRequests = (int)$redis->incr($key); + $countRequests = (int)Yii::$app->redis->incr($key); if ($countRequests === 1) { - $redis->executeCommand('EXPIRE', [$key, $this->limitTime]); + Yii::$app->redis->expire($key, $this->limitTime); } if ($countRequests > $this->limit) { @@ -67,13 +66,6 @@ class RateLimiter extends \yii\filters\RateLimiter { } } - /** - * @return \common\components\Redis\Connection - */ - public function getRedis() { - return Yii::$app->redis; - } - /** * @param Request $request * @return OauthClient|null diff --git a/api/modules/session/models/SessionModel.php b/api/modules/session/models/SessionModel.php index ed5e626..7d37728 100644 --- a/api/modules/session/models/SessionModel.php +++ b/api/modules/session/models/SessionModel.php @@ -19,7 +19,7 @@ class SessionModel { public static function find(string $username, string $serverId): ?self { $key = static::buildKey($username, $serverId); - $result = Yii::$app->redis->executeCommand('GET', [$key]); + $result = Yii::$app->redis->get($key); if (!$result) { return null; } @@ -36,11 +36,11 @@ class SessionModel { 'serverId' => $this->serverId, ]); - return Yii::$app->redis->executeCommand('SETEX', [$key, self::KEY_TIME, $data]); + return Yii::$app->redis->setex($key, self::KEY_TIME, $data); } public function delete() { - return Yii::$app->redis->executeCommand('DEL', [static::buildKey($this->username, $this->serverId)]); + return Yii::$app->redis->del(static::buildKey($this->username, $this->serverId)); } public function getAccount(): ?Account { diff --git a/autocompletion.php b/autocompletion.php index 07b6667..a70d827 100644 --- a/autocompletion.php +++ b/autocompletion.php @@ -16,16 +16,15 @@ class Yii extends \yii\BaseYii { * Class BaseApplication * Used for properties that are identical for both WebApplication and ConsoleApplication * - * @property \yii\db\Connection $unbufferedDb - * @property \yii\swiftmailer\Mailer $mailer - * @property \common\components\Redis\Connection $redis - * @property \common\components\RabbitMQ\Component $amqp - * @property \GuzzleHttp\Client $guzzle - * @property \common\components\EmailRenderer $emailRenderer - * @property \mito\sentry\Component $sentry - * @property \api\components\OAuth2\Component $oauth - * @property \common\components\StatsD $statsd - * @property \yii\queue\Queue $queue + * @property \yii\db\Connection $unbufferedDb + * @property \yii\swiftmailer\Mailer $mailer + * @property \yii\redis\Connection $redis + * @property \GuzzleHttp\Client $guzzle + * @property \common\components\EmailRenderer $emailRenderer + * @property \mito\sentry\Component $sentry + * @property \api\components\OAuth2\Component $oauth + * @property \common\components\StatsD $statsd + * @property \yii\queue\Queue $queue */ abstract class BaseApplication extends yii\base\Application { } diff --git a/common/components/RabbitMQ/Component.php b/common/components/RabbitMQ/Component.php deleted file mode 100644 index b7e6177..0000000 --- a/common/components/RabbitMQ/Component.php +++ /dev/null @@ -1,177 +0,0 @@ - - * - * @property AMQPStreamConnection $connection AMQP connection. - * @property AMQPChannel $channel AMQP channel. - */ -class Component extends \yii\base\Component { - - public const TYPE_TOPIC = 'topic'; - public const TYPE_DIRECT = 'direct'; - public const TYPE_HEADERS = 'headers'; - public const TYPE_FANOUT = 'fanout'; - - /** - * @var string - */ - public $host = '127.0.0.1'; - - /** - * @var integer - */ - public $port = 5672; - - /** - * @var string - */ - public $user; - - /** - * @var string - */ - public $password; - - /** - * @var string - */ - public $vhost = '/'; - - /** - * @var AMQPStreamConnection - */ - protected $amqpConnection; - - /** - * @var AMQPChannel[] - */ - protected $channels = []; - - /** - * @inheritdoc - */ - public function init() { - parent::init(); - if (empty($this->user)) { - throw new Exception("Parameter 'user' was not set for AMQP connection."); - } - } - - /** - * @return AMQPStreamConnection - */ - public function getConnection() { - if (!$this->amqpConnection) { - $this->amqpConnection = new AMQPStreamConnection( - $this->host, - $this->port, - $this->user, - $this->password, - $this->vhost - ); - } - - return $this->amqpConnection; - } - - /** - * @param string $channel_id - * @return AMQPChannel - */ - public function getChannel($channel_id = null) { - $index = $channel_id ?: 'default'; - if (!array_key_exists($index, $this->channels)) { - $this->channels[$index] = $this->getConnection()->channel($channel_id); - } - - return $this->channels[$index]; - } - - // TODO: метод sendToQueue - - /** - * Sends message to the exchange. - * - * @param string $exchangeName - * @param string $routingKey - * @param string|array $message - * @param array $exchangeArgs - * @param array $publishArgs - */ - public function sendToExchange($exchangeName, $routingKey, $message, $exchangeArgs = [], $publishArgs = []) { - $message = $this->prepareMessage($message); - $channel = $this->getChannel(); - $channel->exchange_declare(...$this->prepareExchangeArgs($exchangeName, $exchangeArgs)); - $channel->basic_publish(...$this->preparePublishArgs($message, $exchangeName, $routingKey, $publishArgs)); - } - - /** - * Returns prepaired AMQP message. - * - * @param string|array|object $message - * @param array $properties - * @return AMQPMessage - * @throws Exception If message is empty. - */ - public function prepareMessage($message, $properties = null) { - if ($message instanceof AMQPMessage) { - return $message; - } - - if (empty($message)) { - throw new Exception('AMQP message can not be empty'); - } - - if (is_array($message) || is_object($message)) { - $message = Json::encode($message); - } - - return new AMQPMessage($message, $properties); - } - - /** - * Объединяет переданный набор аргументов с поведением по умолчанию - * - * @param string $exchangeName - * @param array $args - * @return array - */ - protected function prepareExchangeArgs($exchangeName, array $args) { - return array_replace([ - $exchangeName, - self::TYPE_FANOUT, - false, - false, - false, - ], $args); - } - - /** - * Объединяет переданный набор аргументов с поведением по умолчанию - * - * @param AMQPMessage $message - * @param string $exchangeName - * @param string $routeKey - * @param array $args - * - * @return array - */ - protected function preparePublishArgs($message, $exchangeName, $routeKey, array $args) { - return array_replace([ - $message, - $exchangeName, - $routeKey, - ], $args); - } - -} diff --git a/common/components/RabbitMQ/Helper.php b/common/components/RabbitMQ/Helper.php deleted file mode 100644 index e3b5f7a..0000000 --- a/common/components/RabbitMQ/Helper.php +++ /dev/null @@ -1,26 +0,0 @@ -amqp; - } - - public static function sendToExchange($exchange, $routingKey, $message, $exchangeArgs = []) { - static::getInstance()->sendToExchange($exchange, $routingKey, $message, $exchangeArgs); - } - - public static function sendToEventsExchange($routingKey, $message) { - static::sendToExchange('events', $routingKey, $message, [ - 1 => Component::TYPE_TOPIC, // type -> topic - 3 => true, // durable -> true - ]); - } - -} diff --git a/common/components/Redis/Cache.php b/common/components/Redis/Cache.php deleted file mode 100644 index 6a120a1..0000000 --- a/common/components/Redis/Cache.php +++ /dev/null @@ -1,13 +0,0 @@ -redis = Instance::ensure($this->redis, ConnectionInterface::class); - } - -} diff --git a/common/components/Redis/Connection.php b/common/components/Redis/Connection.php deleted file mode 100644 index 28d62c4..0000000 --- a/common/components/Redis/Connection.php +++ /dev/null @@ -1,415 +0,0 @@ -executeCommand($name, $params); - } - - return parent::__call($name, $params); - } - - public function getConnection(): ClientInterface { - if ($this->_client === null) { - $this->_client = new Client($this->prepareParams(), $this->options); - } - - return $this->_client; - } - - public function executeCommand(string $name, array $params = []) { - return $this->getConnection()->$name(...$params); - } - - private function prepareParams() { - if ($this->parameters !== null) { - return $this->parameters; - } - - if ($this->unixSocket) { - $parameters = [ - 'scheme' => 'unix', - 'path' => $this->unixSocket, - ]; - } else { - $parameters = [ - 'scheme' => 'tcp', - 'host' => $this->hostname, - 'port' => $this->port, - ]; - } - - return array_merge($parameters, [ - 'database' => $this->database, - ]); - } - -} diff --git a/common/components/Redis/ConnectionInterface.php b/common/components/Redis/ConnectionInterface.php deleted file mode 100644 index f4195fe..0000000 --- a/common/components/Redis/ConnectionInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -key = $this->buildKey($key); } - public function getRedis(): Connection { - return Yii::$app->redis; - } - public function getKey(): string { return $this->key; } public function getValue() { - return $this->getRedis()->get($this->key); + return Yii::$app->redis->get($this->key); } public function setValue($value): self { - $this->getRedis()->set($this->key, $value); - + Yii::$app->redis->set($this->key, $value); return $this; } public function delete(): self { - $this->getRedis()->del([$this->getKey()]); - + Yii::$app->redis->del($this->getKey()); return $this; } public function exists(): bool { - return (bool)$this->getRedis()->exists($this->key); + return (bool)Yii::$app->redis->exists($this->key); } public function expire(int $ttl): self { - $this->getRedis()->expire($this->key, $ttl); - + Yii::$app->redis->expire($this->key, $ttl); return $this; } public function expireAt(int $unixTimestamp): self { - $this->getRedis()->expireat($this->key, $unixTimestamp); - + Yii::$app->redis->expireat($this->key, $unixTimestamp); return $this; } diff --git a/common/components/Redis/Set.php b/common/components/Redis/Set.php index b106304..b6a07ec 100644 --- a/common/components/Redis/Set.php +++ b/common/components/Redis/Set.php @@ -3,23 +3,22 @@ namespace common\components\Redis; use ArrayIterator; use IteratorAggregate; +use Yii; class Set extends Key implements IteratorAggregate { public function add($value): self { - $this->getRedis()->sadd($this->getKey(), $value); - + Yii::$app->redis->sadd($this->getKey(), $value); return $this; } public function remove($value): self { - $this->getRedis()->srem($this->getKey(), $value); - + Yii::$app->redis->srem($this->getKey(), $value); return $this; } public function members(): array { - return $this->getRedis()->smembers($this->getKey()); + return Yii::$app->redis->smembers($this->getKey()); } public function getValue(): array { @@ -31,11 +30,11 @@ class Set extends Key implements IteratorAggregate { return parent::exists(); } - return (bool)$this->getRedis()->sismember($this->getKey(), $value); + return (bool)Yii::$app->redis->sismember($this->getKey(), $value); } public function diff(array $sets): array { - return $this->getRedis()->sdiff([$this->getKey(), implode(' ', $sets)]); + return Yii::$app->redis->sdiff([$this->getKey(), implode(' ', $sets)]); } /** diff --git a/common/config/config.php b/common/config/config.php index 36b1266..cd71980 100644 --- a/common/config/config.php +++ b/common/config/config.php @@ -4,8 +4,7 @@ return [ 'vendorPath' => dirname(__DIR__, 2) . '/vendor', 'components' => [ 'cache' => [ - 'class' => common\components\Redis\Cache::class, - 'redis' => 'redis', + 'class' => yii\redis\Cache::class, ], 'db' => [ 'class' => yii\db\Connection::class, @@ -61,20 +60,12 @@ return [ 'passwordHashStrategy' => 'password_hash', ], 'redis' => [ - 'class' => common\components\Redis\Connection::class, + 'class' => yii\redis\Connection::class, 'hostname' => getenv('REDIS_HOST') ?: 'redis', 'password' => getenv('REDIS_PASS') ?: null, 'port' => getenv('REDIS_PORT') ?: 6379, 'database' => getenv('REDIS_DATABASE') ?: 0, ], - 'amqp' => [ - 'class' => common\components\RabbitMQ\Component::class, - 'host' => getenv('RABBITMQ_HOST') ?: 'rabbitmq', - 'port' => getenv('RABBITMQ_PORT') ?: 5672, - 'user' => getenv('RABBITMQ_USER'), - 'password' => getenv('RABBITMQ_PASS'), - 'vhost' => getenv('RABBITMQ_VHOST'), - ], 'guzzle' => [ 'class' => GuzzleHttp\Client::class, ], @@ -97,15 +88,7 @@ return [ 'namespace' => getenv('STATSD_NAMESPACE') ?: 'ely.accounts.' . gethostname() . '.app', ], 'queue' => [ - 'class' => yii\queue\amqp_interop\Queue::class, - 'driver' => yii\queue\amqp_interop\Queue::ENQUEUE_AMQP_LIB, - 'host' => getenv('RABBITMQ_HOST') ?: 'rabbitmq', - 'port' => getenv('RABBITMQ_PORT') ?: 5672, - 'user' => getenv('RABBITMQ_USER'), - 'password' => getenv('RABBITMQ_PASS'), - 'vhost' => getenv('RABBITMQ_VHOST'), - 'queueName' => 'worker', - 'exchangeName' => 'tasks', + 'class' => yii\queue\redis\Queue::class, ], ], 'container' => [ diff --git a/common/models/Account.php b/common/models/Account.php index 06d1961..dfc8dfc 100644 --- a/common/models/Account.php +++ b/common/models/Account.php @@ -1,7 +1,10 @@ registration_ip === null ? null : inet_ntop($this->registration_ip); } + public function afterSave($insert, $changedAttributes) { + parent::afterSave($insert, $changedAttributes); + + if ($insert) { + return; + } + + $meaningfulFields = ['username', 'email', 'uuid', 'status', 'lang']; + $meaningfulChangedAttributes = array_filter($changedAttributes, function(string $key) use ($meaningfulFields) { + return in_array($key, $meaningfulFields, true); + }, ARRAY_FILTER_USE_KEY); + if (empty($meaningfulChangedAttributes)) { + return; + } + + Yii::$app->queue->push(CreateWebHooksDeliveries::createAccountEdit($this, $meaningfulChangedAttributes)); + } + } diff --git a/common/tasks/ClearAccountSessions.php b/common/tasks/ClearAccountSessions.php new file mode 100644 index 0000000..86a5043 --- /dev/null +++ b/common/tasks/ClearAccountSessions.php @@ -0,0 +1,64 @@ +accountId = $account->id; + + return $result; + } + + /** + * @return int time to reserve in seconds + */ + public function getTtr(): int { + return 5 * 60; + } + + /** + * @param int $attempt number + * @param \Exception|\Throwable $error from last execute of the job + * + * @return bool + */ + public function canRetry($attempt, $error): bool { + return true; + } + + /** + * @param \yii\queue\Queue $queue which pushed and is handling the job + * @throws \Exception + */ + public function execute($queue): void { + $account = Account::findOne($this->accountId); + if ($account === null) { + return; + } + + foreach ($account->getSessions()->each(100, Yii::$app->unbufferedDb) as $authSession) { + /** @var \common\models\AccountSession $authSession */ + $authSession->delete(); + } + + foreach ($account->getMinecraftAccessKeys()->each(100, Yii::$app->unbufferedDb) as $key) { + /** @var \common\models\MinecraftAccessKey $key */ + $key->delete(); + } + + foreach ($account->getOauthSessions()->each(100, Yii::$app->unbufferedDb) as $oauthSession) { + /** @var \common\models\OauthSession $oauthSession */ + $oauthSession->delete(); + } + } + +} diff --git a/common/tasks/CreateWebHooksDeliveries.php b/common/tasks/CreateWebHooksDeliveries.php new file mode 100644 index 0000000..8ba6fef --- /dev/null +++ b/common/tasks/CreateWebHooksDeliveries.php @@ -0,0 +1,76 @@ +type = 'account.edit'; + $result->payloads = [ + 'id' => $account->id, + 'uuid' => $account->uuid, + 'username' => $account->username, + 'email' => $account->email, + 'lang' => $account->lang, + 'isActive' => $account->status === Account::STATUS_ACTIVE, + 'registered' => date('c', (int)$account->created_at), + 'changedAttributes' => $changedAttributes, + ]; + + return $result; + } + + /** + * @return int time to reserve in seconds + */ + public function getTtr() { + return 10; + } + + /** + * @param int $attempt number + * @param \Exception|\Throwable $error from last execute of the job + * + * @return bool + */ + public function canRetry($attempt, $error) { + return true; + } + + /** + * @param \yii\queue\Queue $queue which pushed and is handling the job + */ + public function execute($queue) { + /** @var WebHook[] $targets */ + $targets = WebHook::find() + ->joinWith('events e', false) + ->andWhere(['e.event_type' => $this->type]) + ->all(); + foreach ($targets as $target) { + $job = new DeliveryWebHook(); + $job->type = $this->type; + $job->url = $target->url; + $job->secret = $target->secret; + $job->payloads = $this->payloads; + Yii::$app->queue->push($job); + } + } + +} diff --git a/common/tasks/DeliveryWebHook.php b/common/tasks/DeliveryWebHook.php new file mode 100644 index 0000000..7561d2a --- /dev/null +++ b/common/tasks/DeliveryWebHook.php @@ -0,0 +1,110 @@ += 5) { + return false; + } + + if ($error instanceof ServerException || $error instanceof ConnectException) { + return true; + } + + return false; + } + + /** + * @param \yii\queue\Queue $queue which pushed and is handling the job + * + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function execute($queue): void { + $client = $this->createClient(); + try { + $client->request('POST', $this->url, [ + 'headers' => [ + 'User-Agent' => 'Account-Ely-Hookshot/' . Yii::$app->version, + 'X-Ely-Accounts-Event' => $this->type, + ], + 'form_params' => $this->payloads, + ]); + } catch (ClientException $e) { + Yii::info("Delivery for {$this->url} has failed with {$e->getResponse()->getStatusCode()} status."); + return; + } + } + + protected function createClient(): ClientInterface { + return new GuzzleClient([ + 'handler' => $this->createStack(), + 'timeout' => 60, + 'connect_timeout' => 10, + ]); + } + + protected function createStack(): HandlerStack { + $stack = HandlerStack::create(); + $stack->push(Middleware::mapRequest(function(RequestInterface $request): RequestInterface { + if (empty($this->secret)) { + return $request; + } + + $payload = (string)$request->getBody(); + $signature = hash_hmac('sha1', $payload, $this->secret); + + /** @noinspection ExceptionsAnnotatingAndHandlingInspection */ + return $request->withHeader('X-Hub-Signature', 'sha1=' . $signature); + })); + + return $stack; + } + +} diff --git a/common/tasks/PullMojangUsername.php b/common/tasks/PullMojangUsername.php new file mode 100644 index 0000000..169ddfb --- /dev/null +++ b/common/tasks/PullMojangUsername.php @@ -0,0 +1,72 @@ +username = $account->username; + + return $result; + } + + /** + * @param \yii\queue\Queue $queue which pushed and is handling the job + * + * @throws \Exception + */ + public function execute($queue) { + Yii::$app->statsd->inc('queue.pullMojangUsername.attempt'); + $mojangApi = $this->createMojangApi(); + try { + $response = $mojangApi->usernameToUUID($this->username); + Yii::$app->statsd->inc('queue.pullMojangUsername.found'); + } catch (NoContentException $e) { + $response = false; + Yii::$app->statsd->inc('queue.pullMojangUsername.not_found'); + } catch (RequestException | MojangApiException $e) { + Yii::$app->statsd->inc('queue.pullMojangUsername.error'); + return; + } + + /** @var MojangUsername|null $mojangUsername */ + $mojangUsername = MojangUsername::findOne($this->username); + if ($response === false) { + if ($mojangUsername !== null) { + $mojangUsername->delete(); + } + } else { + if ($mojangUsername === null) { + $mojangUsername = new MojangUsername(); + $mojangUsername->username = $response->name; + $mojangUsername->uuid = $response->id; + } else { + $mojangUsername->uuid = $response->id; + $mojangUsername->touch('last_pulled_at'); + } + + if (!$mojangUsername->save()) { + throw new ThisShouldNotHappenException('Cannot save mojang username'); + } + } + } + + protected function createMojangApi(): MojangApi { + return new MojangApi(); + } + +} diff --git a/composer.json b/composer.json index 0b2d338..8afc205 100644 --- a/composer.json +++ b/composer.json @@ -13,12 +13,9 @@ "league/oauth2-server": "^4.1", "yiisoft/yii2-redis": "~2.0.0", "guzzlehttp/guzzle": "^6.0.0", - "php-amqplib/php-amqplib": "^2.6.2", "ely/yii2-tempmail-validator": "^2.0", "emarref/jwt": "~1.0.3", - "ely/amqp-controller": "dev-master#d7f8cdbc66c45e477c9c7d5d509bc0c1b11fd3ec", "ely/email-renderer": "dev-master#8aa2e71c5b3b8e4a726c3c090b2997030ba29f73", - "predis/predis": "^1.0", "mito/yii2-sentry": "^1.0", "spomky-labs/otphp": "^9.0.2", "bacon/bacon-qr-code": "^1.0", @@ -26,8 +23,7 @@ "webmozart/assert": "^1.2.0", "goaop/framework": "~2.2.0", "domnikl/statsd": "^2.6", - "yiisoft/yii2-queue": "~2.0.2", - "enqueue/amqp-lib": "^0.8.11" + "yiisoft/yii2-queue": "~2.1.0" }, "require-dev": { "yiisoft/yii2-debug": "*", @@ -40,17 +36,14 @@ "mockery/mockery": "^1.0.0", "php-mock/php-mock-mockery": "^1.2.0", "friendsofphp/php-cs-fixer": "^2.11", - "ely/php-code-style": "^0.1.0" + "ely/php-code-style": "^0.1.0", + "predis/predis": "^1.1" }, "repositories": [ { "type": "composer", "url": "https://asset-packagist.org" }, - { - "type": "git", - "url": "git@gitlab.ely.by:elyby/amqp-controller.git" - }, { "type": "git", "url": "git@gitlab.ely.by:elyby/email-renderer.git" diff --git a/composer.lock b/composer.lock index 19ec143..44bd2a0 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "63497214afa6b50f50d3bc80fe9eb269", + "content-hash": "b576f6f9babd8e00a7fa6768d56c37e9", "packages": [ { "name": "bacon/bacon-qr-code", @@ -153,7 +153,7 @@ "version": "v1.3.2", "source": { "type": "git", - "url": "https://github.com/bestiejs/punycode.js.git", + "url": "git@github.com:bestiejs/punycode.js.git", "reference": "38c8d3131a82567bfef18da09f7f4db68c84f8a3" }, "dist": { @@ -610,42 +610,6 @@ ], "time": "2017-11-15T23:40:40+00:00" }, - { - "name": "ely/amqp-controller", - "version": "dev-master", - "source": { - "type": "git", - "url": "git@gitlab.ely.by:elyby/amqp-controller.git", - "reference": "d7f8cdbc66c45e477c9c7d5d509bc0c1b11fd3ec" - }, - "require": { - "php-amqplib/php-amqplib": "^2.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Ely\\Amqp\\": "src/" - } - }, - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ely.by team", - "email": "team@ely.by" - }, - { - "name": "ErickSkrauch", - "email": "erickskrauch@ely.by" - } - ], - "homepage": "http://ely.by", - "keywords": [ - "" - ], - "time": "2016-11-15T19:40:20+00:00" - }, { "name": "ely/email-renderer", "version": "dev-master", @@ -779,117 +743,6 @@ "description": "A JWT implementation", "time": "2016-09-05T20:33:06+00:00" }, - { - "name": "enqueue/amqp-lib", - "version": "0.8.21", - "source": { - "type": "git", - "url": "https://github.com/php-enqueue/amqp-lib.git", - "reference": "5a0da2f2eccb2ebda4d0b2526e1753c96cf0ef75" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-enqueue/amqp-lib/zipball/5a0da2f2eccb2ebda4d0b2526e1753c96cf0ef75", - "reference": "5a0da2f2eccb2ebda4d0b2526e1753c96cf0ef75", - "shasum": "" - }, - "require": { - "enqueue/amqp-tools": "^0.8.5@dev", - "php": ">=5.6", - "php-amqplib/php-amqplib": "^2.7@dev", - "queue-interop/amqp-interop": "^0.7@dev", - "queue-interop/queue-interop": "^0.6@dev" - }, - "require-dev": { - "enqueue/enqueue": "^0.8@dev", - "enqueue/null": "^0.8@dev", - "enqueue/test": "^0.8@dev", - "phpunit/phpunit": "~5.4.0", - "queue-interop/queue-spec": "^0.5.3@dev", - "symfony/config": "^2.8|^3|^4", - "symfony/dependency-injection": "^2.8|^3|^4" - }, - "suggest": { - "enqueue/enqueue": "If you'd like to use advanced features like Client abstract layer or Symfony integration features" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.8.x-dev" - } - }, - "autoload": { - "psr-4": { - "Enqueue\\AmqpLib\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Message Queue Amqp Transport", - "homepage": "https://enqueue.forma-pro.com/", - "keywords": [ - "AMQP", - "messaging", - "queue" - ], - "time": "2018-02-16T11:05:22+00:00" - }, - { - "name": "enqueue/amqp-tools", - "version": "0.8.14", - "source": { - "type": "git", - "url": "https://github.com/php-enqueue/amqp-tools.git", - "reference": "f375dee4d8609fca565a80df1c0f238bf0fe774f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-enqueue/amqp-tools/zipball/f375dee4d8609fca565a80df1c0f238bf0fe774f", - "reference": "f375dee4d8609fca565a80df1c0f238bf0fe774f", - "shasum": "" - }, - "require": { - "php": ">=5.6", - "queue-interop/amqp-interop": "^0.7@dev", - "queue-interop/queue-interop": "^0.6@dev" - }, - "require-dev": { - "enqueue/null": "^0.8@dev", - "enqueue/test": "^0.8@dev", - "phpunit/phpunit": "~5.4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.8.x-dev" - } - }, - "autoload": { - "psr-4": { - "Enqueue\\AmqpTools\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Message Queue Amqp Tools", - "homepage": "https://enqueue.forma-pro.com/", - "keywords": [ - "AMQP", - "messaging", - "queue" - ], - "time": "2018-01-10T12:00:35+00:00" - }, { "name": "ezyang/htmlpurifier", "version": "v4.9.3", @@ -1619,127 +1472,6 @@ ], "time": "2017-09-27T21:40:39+00:00" }, - { - "name": "php-amqplib/php-amqplib", - "version": "v2.7.2", - "source": { - "type": "git", - "url": "https://github.com/php-amqplib/php-amqplib.git", - "reference": "dfd3694a86f1a7394d3693485259d4074a6ec79b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/dfd3694a86f1a7394d3693485259d4074a6ec79b", - "reference": "dfd3694a86f1a7394d3693485259d4074a6ec79b", - "shasum": "" - }, - "require": { - "ext-bcmath": "*", - "ext-mbstring": "*", - "php": ">=5.3.0" - }, - "replace": { - "videlalvaro/php-amqplib": "self.version" - }, - "require-dev": { - "phpdocumentor/phpdocumentor": "^2.9", - "phpunit/phpunit": "^4.8", - "scrutinizer/ocular": "^1.1", - "squizlabs/php_codesniffer": "^2.5" - }, - "suggest": { - "ext-sockets": "Use AMQPSocketConnection" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "PhpAmqpLib\\": "PhpAmqpLib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-or-later" - ], - "authors": [ - { - "name": "Alvaro Videla", - "role": "Original Maintainer" - }, - { - "name": "John Kelly", - "email": "johnmkelly86@gmail.com", - "role": "Maintainer" - }, - { - "name": "Raúl Araya", - "email": "nubeiro@gmail.com", - "role": "Maintainer" - } - ], - "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", - "homepage": "https://github.com/php-amqplib/php-amqplib/", - "keywords": [ - "message", - "queue", - "rabbitmq" - ], - "time": "2018-02-11T19:28:00+00:00" - }, - { - "name": "predis/predis", - "version": "v1.1.1", - "source": { - "type": "git", - "url": "https://github.com/nrk/predis.git", - "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nrk/predis/zipball/f0210e38881631afeafb56ab43405a92cafd9fd1", - "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "require-dev": { - "phpunit/phpunit": "~4.8" - }, - "suggest": { - "ext-curl": "Allows access to Webdis when paired with phpiredis", - "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" - }, - "type": "library", - "autoload": { - "psr-4": { - "Predis\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniele Alessandri", - "email": "suppakilla@gmail.com", - "homepage": "http://clorophilla.net" - } - ], - "description": "Flexible and feature-complete Redis client for PHP and HHVM", - "homepage": "http://github.com/nrk/predis", - "keywords": [ - "nosql", - "predis", - "redis" - ], - "time": "2016-06-16T16:22:20+00:00" - }, { "name": "psr/http-message", "version": "1.0.1", @@ -1790,87 +1522,6 @@ ], "time": "2016-08-06T14:39:51+00:00" }, - { - "name": "queue-interop/amqp-interop", - "version": "0.7.2", - "source": { - "type": "git", - "url": "https://github.com/queue-interop/amqp-interop.git", - "reference": "03cfac42483d07ab45d1896a6a2e1d873a216bba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/queue-interop/amqp-interop/zipball/03cfac42483d07ab45d1896a6a2e1d873a216bba", - "reference": "03cfac42483d07ab45d1896a6a2e1d873a216bba", - "shasum": "" - }, - "require": { - "php": ">=5.5", - "queue-interop/queue-interop": "^0.6@dev" - }, - "require-dev": { - "phpunit/phpunit": "~5.4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.7.x-dev" - } - }, - "autoload": { - "psr-4": { - "Interop\\Amqp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "time": "2018-01-04T09:52:06+00:00" - }, - { - "name": "queue-interop/queue-interop", - "version": "0.6.1", - "source": { - "type": "git", - "url": "https://github.com/queue-interop/queue-interop.git", - "reference": "38579005c0492c0275bbae31170edf30a7e740fa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/queue-interop/queue-interop/zipball/38579005c0492c0275bbae31170edf30a7e740fa", - "reference": "38579005c0492c0275bbae31170edf30a7e740fa", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.6.x-dev" - } - }, - "autoload": { - "psr-4": { - "Interop\\Queue\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Promoting the interoperability of MQs objects. Based on Java JMS", - "homepage": "https://github.com/queue-interop/queue-interop", - "keywords": [ - "MQ", - "jms", - "message queue", - "messaging", - "queue" - ], - "time": "2017-08-10T11:24:15+00:00" - }, { "name": "ramsey/uuid", "version": "3.7.3", @@ -2701,24 +2352,25 @@ }, { "name": "yiisoft/yii2-queue", - "version": "2.0.2", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-queue.git", - "reference": "8c2b337f7d9ea934c2affdfc21c9fb387d0a0773" + "reference": "d04b4b3c932081200876a351cc6c3502e89e11b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-queue/zipball/8c2b337f7d9ea934c2affdfc21c9fb387d0a0773", - "reference": "8c2b337f7d9ea934c2affdfc21c9fb387d0a0773", + "url": "https://api.github.com/repos/yiisoft/yii2-queue/zipball/d04b4b3c932081200876a351cc6c3502e89e11b8", + "reference": "d04b4b3c932081200876a351cc6c3502e89e11b8", "shasum": "" }, "require": { "php": ">=5.5.0", "symfony/process": "*", - "yiisoft/yii2": "~2.0.13" + "yiisoft/yii2": "~2.0.14" }, "require-dev": { + "aws/aws-sdk-php": ">=2.4", "enqueue/amqp-lib": "^0.8", "jeremeamia/superclosure": "*", "pda/pheanstalk": "*", @@ -2729,6 +2381,7 @@ "yiisoft/yii2-redis": "*" }, "suggest": { + "aws/aws-sdk-php": "Need for aws SQS.", "enqueue/amqp-lib": "Need for AMQP interop queue.", "ext-gearman": "Need for Gearman queue.", "ext-pcntl": "Need for process signals.", @@ -2752,7 +2405,8 @@ "yii\\queue\\file\\": "src/drivers/file", "yii\\queue\\gearman\\": "src/drivers/gearman", "yii\\queue\\redis\\": "src/drivers/redis", - "yii\\queue\\sync\\": "src/drivers/sync" + "yii\\queue\\sync\\": "src/drivers/sync", + "yii\\queue\\sqs\\": "src/drivers/sqs" } }, "notification-url": "https://packagist.org/downloads/", @@ -2765,7 +2419,7 @@ "email": "zhuravljov@gmail.com" } ], - "description": "Yii2 Queue Extension which supported DB, Redis, RabbitMQ, Beanstalk and Gearman", + "description": "Yii2 Queue Extension which supported DB, Redis, RabbitMQ, Beanstalk, SQS and Gearman", "keywords": [ "async", "beanstalk", @@ -2775,9 +2429,10 @@ "queue", "rabbitmq", "redis", + "sqs", "yii" ], - "time": "2017-12-26T17:16:14+00:00" + "time": "2018-05-23T21:04:57+00:00" }, { "name": "yiisoft/yii2-redis", @@ -4657,6 +4312,56 @@ ], "time": "2018-01-06T05:45:45+00:00" }, + { + "name": "predis/predis", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/nrk/predis.git", + "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nrk/predis/zipball/f0210e38881631afeafb56ab43405a92cafd9fd1", + "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "ext-curl": "Allows access to Webdis when paired with phpiredis", + "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" + }, + "type": "library", + "autoload": { + "psr-4": { + "Predis\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniele Alessandri", + "email": "suppakilla@gmail.com", + "homepage": "http://clorophilla.net" + } + ], + "description": "Flexible and feature-complete Redis client for PHP and HHVM", + "homepage": "http://github.com/nrk/predis", + "keywords": [ + "nosql", + "predis", + "redis" + ], + "time": "2016-06-16T16:22:20+00:00" + }, { "name": "sebastian/code-unit-reverse-lookup", "version": "1.0.1", @@ -6026,7 +5731,6 @@ "minimum-stability": "stable", "stability-flags": { "roave/security-advisories": 20, - "ely/amqp-controller": 20, "ely/email-renderer": 20 }, "prefer-stable": false, diff --git a/console/controllers/AccountQueueController.php b/console/controllers/AccountQueueController.php deleted file mode 100644 index 04ee523..0000000 --- a/console/controllers/AccountQueueController.php +++ /dev/null @@ -1,98 +0,0 @@ -exchange->topic()->durable(); - $configurator->queue->name('accounts-accounts-events')->durable(); - $configurator->bind->routingKey('accounts.username-changed') - ->add()->routingKey('account.account-banned'); - } - - public function getRoutesMap() { - return [ - 'accounts.username-changed' => 'routeUsernameChanged', - 'accounts.account-banned' => 'routeAccountBanned', - ]; - } - - public function routeUsernameChanged(UsernameChanged $body): bool { - Yii::$app->statsd->inc('worker.account.usernameChanged.attempt'); - $mojangApi = $this->createMojangApi(); - try { - $response = $mojangApi->usernameToUUID($body->newUsername); - Yii::$app->statsd->inc('worker.account.usernameChanged.found'); - } catch (NoContentException $e) { - $response = false; - Yii::$app->statsd->inc('worker.account.usernameChanged.not_found'); - } catch (RequestException $e) { - return true; - } - - /** @var MojangUsername|null $mojangUsername */ - $mojangUsername = MojangUsername::findOne($body->newUsername); - if ($response === false) { - if ($mojangUsername !== null) { - $mojangUsername->delete(); - } - } else { - if ($mojangUsername === null) { - $mojangUsername = new MojangUsername(); - $mojangUsername->username = $response->name; - $mojangUsername->uuid = $response->id; - } else { - $mojangUsername->uuid = $response->id; - $mojangUsername->touch('last_pulled_at'); - } - - $mojangUsername->save(); - } - - return true; - } - - public function routeAccountBanned(AccountBanned $body): bool { - $account = Account::findOne($body->accountId); - if ($account === null) { - Yii::warning('Cannot find banned account ' . $body->accountId . '. Skipping.'); - return true; - } - - foreach ($account->sessions as $authSession) { - $authSession->delete(); - } - - foreach ($account->minecraftAccessKeys as $key) { - $key->delete(); - } - - foreach ($account->oauthSessions as $oauthSession) { - $oauthSession->delete(); - } - - return true; - } - - /** - * @return MojangApi - */ - protected function createMojangApi(): MojangApi { - return new MojangApi(); - } - -} diff --git a/console/controllers/AmqpController.php b/console/controllers/AmqpController.php deleted file mode 100644 index 1c6360c..0000000 --- a/console/controllers/AmqpController.php +++ /dev/null @@ -1,72 +0,0 @@ -start(); - } - - public function getRoutesMap() { - return []; - } - - /** - * Переопределяем метод callback, чтобы избержать логгирования в консоль ошибок, - * связанных с обвалом того или иного соединения. Это нормально, PHP рождён умирать, - * а не работать 24/7 в качестве демона. - * - * @param AMQPMessage $msg - * @throws YiiDbException - */ - public function callback(AMQPMessage $msg) { - try { - $this->_callback($msg); - } catch (YiiDbException $e) { - if ($this->reconnected || !$this->isRestorableException($e)) { - throw $e; - } - - $this->reconnected = true; - Yii::$app->db->close(); - Yii::$app->db->open(); - $this->callback($msg); - } - - $this->reconnected = false; - } - - /** - * @inheritdoc - */ - protected function getConnection() { - return Yii::$app->amqp->getConnection(); - } - - /** - * @inheritdoc - */ - protected function buildRouteActionName($route) { - return ArrayHelper::getValue($this->getRoutesMap(), $route, 'route' . Inflector::camelize($route)); - } - - private function isRestorableException(Exception $e): bool { - return strpos($e->getMessage(), 'MySQL server has gone away') !== false - || strcmp($e->getMessage(), 'Error while sending QUERY packet') !== false; - } - -} diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index f93f1f7..7082ef4 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -7,7 +7,18 @@ services: depends_on: - db - redis - - rabbitmq + volumes: + - ./:/var/www/html/ + env_file: .env + + worker: + build: + dockerfile: Dockerfile-dev + context: . + command: ['php', 'yii', 'queue/listen', '-v'] + depends_on: + - db + - redis volumes: - ./:/var/www/html/ env_file: .env @@ -34,16 +45,6 @@ services: volumes: - ./data/redis:/data - rabbitmq: - image: rabbitmq:3.6-management - env_file: .env - environment: - - VIRTUAL_HOST=rabbitmq.account.ely.by.local - - VIRTUAL_PORT=15672 - networks: - - default - - nginx-proxy - phpmyadmin: build: ./docker/phpmyadmin environment: diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 47b6e78..539c878 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -2,14 +2,24 @@ version: '2' services: app: image: registry.ely.by/elyby/accounts:latest + restart: always + depends_on: + - db + - redis + env_file: .env + + worker: + image: registry.ely.by/elyby/accounts:latest + restart: always + command: ['php', 'yii', 'queue/listen', '-v'] depends_on: - db - redis - - rabbitmq env_file: .env web: image: registry.ely.by/elyby/accounts-nginx:1.0.3 + restart: always volumes_from: - app links: @@ -21,19 +31,17 @@ services: db: build: ./docker/mariadb + restart: always env_file: .env volumes: - ./data/mysql:/var/lib/mysql redis: image: redis:3.0-alpine + restart: always volumes: - ./data/redis:/data - rabbitmq: - image: rabbitmq:3.6 - env_file: .env - networks: nginx-proxy: external: diff --git a/docker/phpmyadmin/Dockerfile b/docker/phpmyadmin/Dockerfile index 62e24d4..6b03cfe 100644 --- a/docker/phpmyadmin/Dockerfile +++ b/docker/phpmyadmin/Dockerfile @@ -1,4 +1,4 @@ -FROM phpmyadmin/phpmyadmin +FROM phpmyadmin/phpmyadmin:4.7.9-1 RUN printf "\n\nrequire('./config.local.php');\n" >> /www/config.inc.php diff --git a/api/models/profile/TwoFactorAuthForm.php b/docker/supervisor/.gitkeep similarity index 100% rename from api/models/profile/TwoFactorAuthForm.php rename to docker/supervisor/.gitkeep diff --git a/docker/supervisor/account-queue-worker.conf b/docker/supervisor/account-queue-worker.conf deleted file mode 100644 index aed1af3..0000000 --- a/docker/supervisor/account-queue-worker.conf +++ /dev/null @@ -1,6 +0,0 @@ -[program:account-queue-worker] -directory=/var/www/html -command=wait-for-it rabbitmq:5672 -- php yii account-queue -autostart=true -autorestart=true -priority=10 diff --git a/docker/supervisor/worker-queue.conf b/docker/supervisor/worker-queue.conf deleted file mode 100644 index 397c104..0000000 --- a/docker/supervisor/worker-queue.conf +++ /dev/null @@ -1,6 +0,0 @@ -[program:queue-worker] -directory=/var/www/html -command=wait-for-it rabbitmq:5672 -- php yii queue/listen -v -autostart=true -autorestart=true -priority=10 diff --git a/tests/codeception/api/functional.suite.yml b/tests/codeception/api/functional.suite.yml index 9a6ac97..570a025 100644 --- a/tests/codeception/api/functional.suite.yml +++ b/tests/codeception/api/functional.suite.yml @@ -4,7 +4,6 @@ modules: - Filesystem - Yii2 - tests\codeception\common\_support\FixtureHelper - - tests\codeception\common\_support\amqp\Helper - tests\codeception\common\_support\Mockery - Redis - Asserts diff --git a/tests/codeception/api/unit.suite.yml b/tests/codeception/api/unit.suite.yml index 7de7236..beea248 100644 --- a/tests/codeception/api/unit.suite.yml +++ b/tests/codeception/api/unit.suite.yml @@ -3,7 +3,6 @@ modules: enabled: - Yii2: part: [orm, email, fixtures] - - tests\codeception\common\_support\amqp\Helper - tests\codeception\common\_support\queue\CodeceptionQueueHelper - tests\codeception\common\_support\Mockery config: diff --git a/tests/codeception/api/unit/models/authentication/ConfirmEmailFormTest.php b/tests/codeception/api/unit/models/authentication/ConfirmEmailFormTest.php index 404b994..c392830 100644 --- a/tests/codeception/api/unit/models/authentication/ConfirmEmailFormTest.php +++ b/tests/codeception/api/unit/models/authentication/ConfirmEmailFormTest.php @@ -28,12 +28,6 @@ class ConfirmEmailFormTest extends TestCase { /** @var Account $account */ $account = Account::findOne($fixture['account_id']); $this->assertEquals(Account::STATUS_ACTIVE, $account->status, 'user status changed to active'); - - $message = $this->tester->grabLastSentAmqpMessage('events'); - $body = json_decode($message->getBody(), true); - $this->assertEquals($account->id, $body['accountId']); - $this->assertEquals($account->username, $body['newUsername']); - $this->assertNull($body['oldUsername']); } private function createModel($key) { diff --git a/tests/codeception/api/unit/modules/accounts/models/ChangeEmailFormTest.php b/tests/codeception/api/unit/modules/accounts/models/ChangeEmailFormTest.php index eb7f8f5..abf041d 100644 --- a/tests/codeception/api/unit/modules/accounts/models/ChangeEmailFormTest.php +++ b/tests/codeception/api/unit/modules/accounts/models/ChangeEmailFormTest.php @@ -32,19 +32,6 @@ class ChangeEmailFormTest extends TestCase { /** @noinspection UnserializeExploitsInspection */ $data = unserialize($newEmailConfirmationFixture['_data']); $this->assertEquals($data['newEmail'], $account->email); - $this->tester->canSeeAmqpMessageIsCreated('events'); - } - - public function testCreateTask() { - /** @var Account $account */ - $account = Account::findOne($this->getAccountId()); - $model = new ChangeEmailForm($account); - $model->createTask(1, 'test1@ely.by', 'test@ely.by'); - $message = $this->tester->grabLastSentAmqpMessage('events'); - $body = json_decode($message->getBody(), true); - $this->assertEquals(1, $body['accountId']); - $this->assertEquals('test1@ely.by', $body['newEmail']); - $this->assertEquals('test@ely.by', $body['oldEmail']); } private function getAccountId() { diff --git a/tests/codeception/api/unit/modules/accounts/models/ChangeUsernameFormTest.php b/tests/codeception/api/unit/modules/accounts/models/ChangeUsernameFormTest.php index 1931272..67d6db5 100644 --- a/tests/codeception/api/unit/modules/accounts/models/ChangeUsernameFormTest.php +++ b/tests/codeception/api/unit/modules/accounts/models/ChangeUsernameFormTest.php @@ -4,6 +4,7 @@ namespace tests\codeception\api\unit\modules\accounts\models; use api\modules\accounts\models\ChangeUsernameForm; use common\models\Account; use common\models\UsernameHistory; +use common\tasks\PullMojangUsername; use tests\codeception\api\unit\TestCase; use tests\codeception\common\fixtures\AccountFixture; use tests\codeception\common\fixtures\UsernameHistoryFixture; @@ -25,7 +26,10 @@ class ChangeUsernameFormTest extends TestCase { $this->assertTrue($model->performAction()); $this->assertEquals('my_new_nickname', Account::findOne($this->getAccountId())->username); $this->assertInstanceOf(UsernameHistory::class, UsernameHistory::findOne(['username' => 'my_new_nickname'])); - $this->tester->canSeeAmqpMessageIsCreated('events'); + /** @var PullMojangUsername $job */ + $job = $this->tester->grabLastQueuedJob(); + $this->assertInstanceOf(PullMojangUsername::class, $job); + $this->assertSame($job->username, 'my_new_nickname'); } public function testPerformActionWithTheSameUsername() { @@ -42,7 +46,7 @@ class ChangeUsernameFormTest extends TestCase { 'username' => $username, ['>=', 'applied_in', $callTime], ]), 'no new UsernameHistory record, if we don\'t change username'); - $this->tester->cantSeeAmqpMessageIsCreated('events'); + $this->assertNull($this->tester->grabLastQueuedJob()); } public function testPerformActionWithChangeCase() { @@ -58,17 +62,10 @@ class ChangeUsernameFormTest extends TestCase { UsernameHistory::findOne(['username' => $newUsername]), 'username should change, if we change case of some letters' ); - $this->tester->canSeeAmqpMessageIsCreated('events'); - } - - public function testCreateTask() { - $model = new ChangeUsernameForm($this->getAccount()); - $model->createEventTask(1, 'test1', 'test'); - $message = $this->tester->grabLastSentAmqpMessage('events'); - $body = json_decode($message->getBody(), true); - $this->assertEquals(1, $body['accountId']); - $this->assertEquals('test1', $body['newUsername']); - $this->assertEquals('test', $body['oldUsername']); + /** @var PullMojangUsername $job */ + $job = $this->tester->grabLastQueuedJob(); + $this->assertInstanceOf(PullMojangUsername::class, $job); + $this->assertSame($job->username, $newUsername); } private function getAccount(): Account { diff --git a/tests/codeception/api/unit/modules/internal/models/BanFormTest.php b/tests/codeception/api/unit/modules/internal/models/BanFormTest.php index 4cd3c02..3894f7f 100644 --- a/tests/codeception/api/unit/modules/internal/models/BanFormTest.php +++ b/tests/codeception/api/unit/modules/internal/models/BanFormTest.php @@ -4,6 +4,7 @@ namespace tests\codeception\api\unit\modules\internal\models; use api\modules\accounts\models\BanAccountForm; use api\modules\internal\helpers\Error as E; use common\models\Account; +use common\tasks\ClearAccountSessions; use tests\codeception\api\unit\TestCase; class BanFormTest extends TestCase { @@ -35,28 +36,10 @@ class BanFormTest extends TestCase { $model = new BanAccountForm($account); $this->assertTrue($model->performAction()); $this->assertEquals(Account::STATUS_BANNED, $account->status); - $this->tester->canSeeAmqpMessageIsCreated('events'); - } - - public function testCreateTask() { - $account = new Account(); - $account->id = 3; - - $model = new BanAccountForm($account); - $model->createTask(); - $message = json_decode($this->tester->grabLastSentAmqpMessage('events')->body, true); - $this->assertSame(3, $message['accountId']); - $this->assertSame(-1, $message['duration']); - $this->assertSame('', $message['message']); - - $model = new BanAccountForm($account); - $model->duration = 123; - $model->message = 'test'; - $model->createTask(); - $message = json_decode($this->tester->grabLastSentAmqpMessage('events')->body, true); - $this->assertSame(3, $message['accountId']); - $this->assertSame(123, $message['duration']); - $this->assertSame('test', $message['message']); + /** @var ClearAccountSessions $job */ + $job = $this->tester->grabLastQueuedJob(); + $this->assertInstanceOf(ClearAccountSessions::class, $job); + $this->assertSame($job->accountId, $account->id); } } diff --git a/tests/codeception/api/unit/modules/internal/models/PardonFormTest.php b/tests/codeception/api/unit/modules/internal/models/PardonFormTest.php index 1d6e2a7..362271f 100644 --- a/tests/codeception/api/unit/modules/internal/models/PardonFormTest.php +++ b/tests/codeception/api/unit/modules/internal/models/PardonFormTest.php @@ -36,17 +36,6 @@ class PardonFormTest extends TestCase { $model = new PardonAccountForm($account); $this->assertTrue($model->performAction()); $this->assertEquals(Account::STATUS_ACTIVE, $account->status); - $this->tester->canSeeAmqpMessageIsCreated('events'); - } - - public function testCreateTask() { - $account = new Account(); - $account->id = 3; - - $model = new PardonAccountForm($account); - $model->createTask(); - $message = json_decode($this->tester->grabLastSentAmqpMessage('events')->body, true); - $this->assertSame(3, $message['accountId']); } } diff --git a/tests/codeception/common/_support/amqp/Helper.php b/tests/codeception/common/_support/amqp/Helper.php deleted file mode 100644 index e623646..0000000 --- a/tests/codeception/common/_support/amqp/Helper.php +++ /dev/null @@ -1,91 +0,0 @@ -seeAmqpMessageIsCreated(); - * - * // check that only 3 messages were created - * $I->seeAmqpMessageIsCreated(3); - * ``` - * - * @param string|null $exchange - * @param int|null $num - */ - public function seeAmqpMessageIsCreated($exchange = null, $num = null) { - if ($num === null) { - $this->assertNotEmpty($this->grabSentAmqpMessages($exchange), 'message were created'); - return; - } - - $this->assertCount( - $num, - $this->grabSentAmqpMessages($exchange), - 'number of created messages is equal to ' . $num - ); - } - - /** - * Checks that no messages was created - * - * @param string|null $exchange - */ - public function dontSeeAmqpMessageIsCreated($exchange = null) { - $this->seeAmqpMessageIsCreated($exchange, 0); - } - - /** - * Returns last sent message - * - * @param string|null $exchange - * @return \PhpAmqpLib\Message\AMQPMessage - */ - public function grabLastSentAmqpMessage($exchange = null) { - $this->seeAmqpMessageIsCreated(); - $messages = $this->grabSentAmqpMessages($exchange); - - return end($messages); - } - - /** - * Returns array of all sent amqp messages. - * Each message is `\PhpAmqpLib\Message\AMQPMessage` instance. - * Useful to perform additional checks using `Asserts` module. - * - * @param string|null $exchange - * @return \PhpAmqpLib\Message\AMQPMessage[] - * @throws ModuleException - */ - public function grabSentAmqpMessages($exchange = null) { - $amqp = $this->grabComponent('amqp'); - if (!$amqp instanceof TestComponent) { - throw new ModuleException($this, 'AMQP module is not mocked, can\'t test messages'); - } - - return $amqp->getSentMessages($exchange); - } - - private function grabComponent(string $component) { - return $this->getYii2()->grabComponent($component); - } - - private function getYii2(): Yii2 { - $yii2 = $this->getModule('Yii2'); - if (!$yii2 instanceof Yii2) { - throw new ModuleException($this, 'Yii2 module must be configured'); - } - - return $yii2; - } - -} diff --git a/tests/codeception/common/_support/amqp/TestComponent.php b/tests/codeception/common/_support/amqp/TestComponent.php deleted file mode 100644 index ed90730..0000000 --- a/tests/codeception/common/_support/amqp/TestComponent.php +++ /dev/null @@ -1,58 +0,0 @@ -sentMessages[$exchangeName][] = $this->prepareMessage($message); - } - - /** - * @param string|null $exchangeName - * @return \PhpAmqpLib\Message\AMQPMessage[] - */ - public function getSentMessages(string $exchangeName = null): array { - if ($exchangeName !== null) { - return $this->sentMessages[$exchangeName] ?? []; - } - - $messages = []; - foreach ($this->sentMessages as $exchangeGroup) { - foreach ($exchangeGroup as $message) { - $messages[] = $message; - } - } - - return $messages; - } - -} diff --git a/tests/codeception/common/_support/queue/CodeceptionQueueHelper.php b/tests/codeception/common/_support/queue/CodeceptionQueueHelper.php index 8878ea3..326e7f3 100644 --- a/tests/codeception/common/_support/queue/CodeceptionQueueHelper.php +++ b/tests/codeception/common/_support/queue/CodeceptionQueueHelper.php @@ -14,7 +14,12 @@ class CodeceptionQueueHelper extends Module { */ public function grabLastQueuedJob() { $messages = $this->grabQueueJobs(); - return end($messages); + $last = end($messages); + if ($last === false) { + return null; + } + + return $last; } /** diff --git a/tests/codeception/common/fixtures/WebHooksEventsFixture.php b/tests/codeception/common/fixtures/WebHooksEventsFixture.php new file mode 100644 index 0000000..300a7ea --- /dev/null +++ b/tests/codeception/common/fixtures/WebHooksEventsFixture.php @@ -0,0 +1,19 @@ + 1, + 'event_type' => 'account.edit', + ], + [ + 'webhook_id' => 2, + 'event_type' => 'account.edit', + ], +]; diff --git a/tests/codeception/common/fixtures/data/webhooks.php b/tests/codeception/common/fixtures/data/webhooks.php new file mode 100644 index 0000000..238c90a --- /dev/null +++ b/tests/codeception/common/fixtures/data/webhooks.php @@ -0,0 +1,21 @@ + [ + 'id' => 1, + 'url' => 'http://localhost:80/webhooks/ely', + 'secret' => 'my-secret', + 'created_at' => 1531054333, + ], + 'webhook-without-secret' => [ + 'id' => 2, + 'url' => 'http://localhost:81/webhooks/ely', + 'secret' => null, + 'created_at' => 1531054837, + ], + 'webhook-without-events' => [ + 'id' => 3, + 'url' => 'http://localhost:82/webhooks/ely', + 'secret' => null, + 'created_at' => 1531054990, + ], +]; diff --git a/tests/codeception/common/unit.suite.yml b/tests/codeception/common/unit.suite.yml index 11d4bb4..ac95de3 100644 --- a/tests/codeception/common/unit.suite.yml +++ b/tests/codeception/common/unit.suite.yml @@ -3,6 +3,7 @@ modules: enabled: - Yii2: part: [orm, email, fixtures] + - tests\codeception\common\_support\queue\CodeceptionQueueHelper - tests\codeception\common\_support\Mockery config: Yii2: diff --git a/tests/codeception/common/unit/models/AccountTest.php b/tests/codeception/common/unit/models/AccountTest.php index afb5de9..ab9b477 100644 --- a/tests/codeception/common/unit/models/AccountTest.php +++ b/tests/codeception/common/unit/models/AccountTest.php @@ -1,14 +1,20 @@ assertNull($account->getRegistrationIp()); } + public function testAfterSaveInsertEvent() { + $account = new Account(); + $account->afterSave(true, [ + 'username' => 'old-username', + ]); + $this->assertNull($this->tester->grabLastQueuedJob()); + } + + public function testAfterSaveNotMeaningfulAttributes() { + $account = new Account(); + $account->afterSave(false, [ + 'updatedAt' => time(), + ]); + $this->assertNull($this->tester->grabLastQueuedJob()); + } + + public function testAfterSavePushEvent() { + $changedAttributes = [ + 'username' => 'old-username', + 'email' => 'old-email@ely.by', + 'uuid' => 'c3cc0121-fa87-4818-9c0e-4acb7f9a28c5', + 'status' => 10, + 'lang' => 'en', + ]; + + $account = new Account(); + $account->afterSave(false, $changedAttributes); + /** @var CreateWebHooksDeliveries $job */ + $job = $this->tester->grabLastQueuedJob(); + $this->assertInstanceOf(CreateWebHooksDeliveries::class, $job); + $this->assertSame($job->payloads['changedAttributes'], $changedAttributes); + } + } diff --git a/tests/codeception/common/unit/tasks/ClearAccountSessionsTest.php b/tests/codeception/common/unit/tasks/ClearAccountSessionsTest.php new file mode 100644 index 0000000..af0e554 --- /dev/null +++ b/tests/codeception/common/unit/tasks/ClearAccountSessionsTest.php @@ -0,0 +1,44 @@ + fixtures\AccountFixture::class, + 'oauthSessions' => fixtures\OauthSessionFixture::class, + 'minecraftAccessKeys' => fixtures\MinecraftAccessKeyFixture::class, + 'authSessions' => fixtures\AccountSessionFixture::class, + ]; + } + + public function testCreateFromAccount() { + $account = new Account(); + $account->id = 123; + $task = ClearAccountSessions::createFromAccount($account); + $this->assertSame(123, $task->accountId); + } + + public function testExecute() { + /** @var \common\models\Account $bannedAccount */ + $bannedAccount = $this->tester->grabFixture('accounts', 'banned-account'); + $task = new ClearAccountSessions(); + $task->accountId = $bannedAccount->id; + $task->execute(mock(Queue::class)); + $this->assertEmpty($bannedAccount->sessions); + $this->assertEmpty($bannedAccount->minecraftAccessKeys); + $this->assertEmpty($bannedAccount->oauthSessions); + } + +} diff --git a/tests/codeception/common/unit/tasks/CreateWebHooksDeliveriesTest.php b/tests/codeception/common/unit/tasks/CreateWebHooksDeliveriesTest.php new file mode 100644 index 0000000..4596a51 --- /dev/null +++ b/tests/codeception/common/unit/tasks/CreateWebHooksDeliveriesTest.php @@ -0,0 +1,91 @@ + fixtures\WebHooksFixture::class, + 'webhooksEvents' => fixtures\WebHooksEventsFixture::class, + ]; + } + + public function testCreateAccountEdit() { + $account = new Account(); + $account->id = 123; + $account->username = 'mock-username'; + $account->uuid = 'afc8dc7a-4bbf-4d3a-8699-68890088cf84'; + $account->email = 'mock@ely.by'; + $account->lang = 'en'; + $account->status = Account::STATUS_ACTIVE; + $account->created_at = 1531008814; + $changedAttributes = [ + 'username' => 'old-username', + 'uuid' => 'e05d33e9-ff91-4d26-9f5c-8250f802a87a', + 'email' => 'old-email@ely.by', + 'status' => 0, + ]; + $result = CreateWebHooksDeliveries::createAccountEdit($account, $changedAttributes); + $this->assertInstanceOf(CreateWebHooksDeliveries::class, $result); + $this->assertSame('account.edit', $result->type); + $this->assertArraySubset([ + 'id' => 123, + 'uuid' => 'afc8dc7a-4bbf-4d3a-8699-68890088cf84', + 'username' => 'mock-username', + 'email' => 'mock@ely.by', + 'lang' => 'en', + 'isActive' => true, + 'registered' => '2018-07-08T00:13:34+00:00', + 'changedAttributes' => $changedAttributes, + ], $result->payloads); + } + + public function testExecute() { + $task = new CreateWebHooksDeliveries(); + $task->type = 'account.edit'; + $task->payloads = [ + 'id' => 123, + 'uuid' => 'afc8dc7a-4bbf-4d3a-8699-68890088cf84', + 'username' => 'mock-username', + 'email' => 'mock@ely.by', + 'lang' => 'en', + 'isActive' => true, + 'registered' => '2018-07-08T00:13:34+00:00', + 'changedAttributes' => [ + 'username' => 'old-username', + 'uuid' => 'e05d33e9-ff91-4d26-9f5c-8250f802a87a', + 'email' => 'old-email@ely.by', + 'status' => 0, + ], + ]; + $task->execute(mock(Queue::class)); + /** @var DeliveryWebHook[] $tasks */ + $tasks = $this->tester->grabQueueJobs(); + $this->assertCount(2, $tasks); + + $this->assertInstanceOf(DeliveryWebHook::class, $tasks[0]); + $this->assertSame($task->type, $tasks[0]->type); + $this->assertSame($task->payloads, $tasks[0]->payloads); + $this->assertSame('http://localhost:80/webhooks/ely', $tasks[0]->url); + $this->assertSame('my-secret', $tasks[0]->secret); + + $this->assertInstanceOf(DeliveryWebHook::class, $tasks[1]); + $this->assertSame($task->type, $tasks[1]->type); + $this->assertSame($task->payloads, $tasks[1]->payloads); + $this->assertSame('http://localhost:81/webhooks/ely', $tasks[1]->url); + $this->assertNull($tasks[1]->secret); + } + +} diff --git a/tests/codeception/common/unit/tasks/DeliveryWebHookTest.php b/tests/codeception/common/unit/tasks/DeliveryWebHookTest.php new file mode 100644 index 0000000..af74384 --- /dev/null +++ b/tests/codeception/common/unit/tasks/DeliveryWebHookTest.php @@ -0,0 +1,132 @@ +assertFalse($task->canRetry(1, new \Exception())); + $request = new Request('POST', 'http://localhost'); + $this->assertTrue($task->canRetry(4, new ConnectException('', $request))); + $this->assertTrue($task->canRetry(4, new ServerException('', $request))); + $this->assertFalse($task->canRetry(5, new ConnectException('', $request))); + $this->assertFalse($task->canRetry(5, new ServerException('', $request))); + } + + public function testExecuteSuccessDelivery() { + $this->response = new Response(); + $task = $this->createMockedTask(); + $task->type = 'account.edit'; + $task->url = 'http://localhost:81/webhooks/ely'; + $task->payloads = [ + 'key' => 'value', + 'another' => 'value', + ]; + $task->execute(mock(Queue::class)); + /** @var Request $request */ + $request = $this->historyContainer[0]['request']; + $this->assertSame('http://localhost:81/webhooks/ely', (string)$request->getUri()); + $this->assertStringStartsWith('Account-Ely-Hookshot/', $request->getHeaders()['User-Agent'][0]); + $this->assertSame('account.edit', $request->getHeaders()['X-Ely-Accounts-Event'][0]); + $this->assertSame('application/x-www-form-urlencoded', $request->getHeaders()['Content-Type'][0]); + $this->assertArrayNotHasKey('X-Hub-Signature', $request->getHeaders()); + $this->assertEquals('key=value&another=value', (string)$request->getBody()); + } + + public function testExecuteSuccessDeliveryWithSignature() { + $this->response = new Response(); + $task = $this->createMockedTask(); + $task->type = 'account.edit'; + $task->url = 'http://localhost:81/webhooks/ely'; + $task->secret = 'secret'; + $task->payloads = [ + 'key' => 'value', + 'another' => 'value', + ]; + $task->execute(mock(Queue::class)); + /** @var Request $request */ + $request = $this->historyContainer[0]['request']; + $this->assertSame('http://localhost:81/webhooks/ely', (string)$request->getUri()); + $this->assertStringStartsWith('Account-Ely-Hookshot/', $request->getHeaders()['User-Agent'][0]); + $this->assertSame('account.edit', $request->getHeaders()['X-Ely-Accounts-Event'][0]); + $this->assertSame('application/x-www-form-urlencoded', $request->getHeaders()['Content-Type'][0]); + $this->assertSame('sha1=3c0b1eef564b2d3a5e9c0f2a8302b1b42b3d4784', $request->getHeaders()['X-Hub-Signature'][0]); + $this->assertEquals('key=value&another=value', (string)$request->getBody()); + } + + public function testExecuteHandleClientException() { + $this->response = new Response(403); + $task = $this->createMockedTask(); + $task->type = 'account.edit'; + $task->url = 'http://localhost:81/webhooks/ely'; + $task->secret = 'secret'; + $task->payloads = [ + 'key' => 'value', + 'another' => 'value', + ]; + $task->execute(mock(Queue::class)); + } + + /** + * @expectedException \GuzzleHttp\Exception\ServerException + */ + public function testExecuteUnhandledException() { + $this->response = new Response(502); + $task = $this->createMockedTask(); + $task->type = 'account.edit'; + $task->url = 'http://localhost:81/webhooks/ely'; + $task->secret = 'secret'; + $task->payloads = [ + 'key' => 'value', + 'another' => 'value', + ]; + $task->execute(mock(Queue::class)); + } + + private function createMockedTask(): DeliveryWebHook { + $container = &$this->historyContainer; + $response = $this->response; + return new class ($container, $response) extends DeliveryWebHook { + private $historyContainer; + + private $response; + + public function __construct(array &$historyContainer, $response) { + $this->historyContainer = &$historyContainer; + $this->response = $response; + } + + protected function createStack(): HandlerStack { + $stack = parent::createStack(); + $stack->setHandler(new MockHandler([$this->response])); + $stack->push(Middleware::history($this->historyContainer)); + + return $stack; + } + }; + } + +} diff --git a/tests/codeception/console/unit/controllers/AccountQueueControllerTest.php b/tests/codeception/common/unit/tasks/PullMojangUsernameTest.php similarity index 50% rename from tests/codeception/console/unit/controllers/AccountQueueControllerTest.php rename to tests/codeception/common/unit/tasks/PullMojangUsernameTest.php index 0fbe3fe..cac50b4 100644 --- a/tests/codeception/console/unit/controllers/AccountQueueControllerTest.php +++ b/tests/codeception/common/unit/tasks/PullMojangUsernameTest.php @@ -1,30 +1,32 @@ AccountFixture::class, 'mojangUsernames' => MojangUsernameFixture::class, ]; } @@ -32,10 +34,9 @@ class AccountQueueControllerTest extends TestCase { public function _before() { parent::_before(); - /** @var AccountQueueController|\PHPUnit_Framework_MockObject_MockObject $controller */ - $controller = $this->getMockBuilder(AccountQueueController::class) + /** @var PullMojangUsername|\PHPUnit_Framework_MockObject_MockObject $task */ + $task = $this->getMockBuilder(PullMojangUsername::class) ->setMethods(['createMojangApi']) - ->setConstructorArgs(['account-queue', Yii::$app]) ->getMock(); /** @var Api|\PHPUnit_Framework_MockObject_MockObject $apiMock */ @@ -54,30 +55,31 @@ class AccountQueueControllerTest extends TestCase { return $this->expectedResponse; }); - $controller + $task ->expects($this->any()) ->method('createMojangApi') ->willReturn($apiMock); - $this->controller = $controller; + $this->task = $task; } - public function testRouteUsernameChangedUsernameExists() { + public function testCreateFromAccount() { + $account = new Account(); + $account->username = 'find-me'; + $result = PullMojangUsername::createFromAccount($account); + $this->assertSame('find-me', $result->username); + } + + public function testExecuteUsernameExists() { $expectedResponse = new UsernameToUUIDResponse(); $expectedResponse->id = '069a79f444e94726a5befca90e38aaf5'; $expectedResponse->name = 'Notch'; $this->expectedResponse = $expectedResponse; - /** @var \common\models\Account $accountInfo */ - $accountInfo = $this->tester->grabFixture('accounts', 'admin'); - /** @var MojangUsername $mojangUsernameFixture */ + /** @var \common\models\MojangUsername $mojangUsernameFixture */ $mojangUsernameFixture = $this->tester->grabFixture('mojangUsernames', 'Notch'); - $body = new UsernameChanged([ - 'accountId' => $accountInfo->id, - 'oldUsername' => $accountInfo->username, - 'newUsername' => 'Notch', - ]); - $this->controller->routeUsernameChanged($body); + $this->task->username = 'Notch'; + $this->task->execute(mock(Queue::class)); /** @var MojangUsername|null $mojangUsername */ $mojangUsername = MojangUsername::findOne('Notch'); $this->assertInstanceOf(MojangUsername::class, $mojangUsername); @@ -85,81 +87,62 @@ class AccountQueueControllerTest extends TestCase { $this->assertLessThanOrEqual(time(), $mojangUsername->last_pulled_at); } - public function testRouteUsernameChangedUsernameNotExists() { + public function testExecuteChangedUsernameExists() { + $expectedResponse = new UsernameToUUIDResponse(); + $expectedResponse->id = '069a79f444e94726a5befca90e38aaf5'; + $expectedResponse->name = 'Notch'; + $this->expectedResponse = $expectedResponse; + + /** @var MojangUsername $mojangUsernameFixture */ + $mojangUsernameFixture = $this->tester->grabFixture('mojangUsernames', 'Notch'); + $this->task->username = 'Notch'; + $this->task->execute(mock(Queue::class)); + /** @var MojangUsername|null $mojangUsername */ + $mojangUsername = MojangUsername::findOne('Notch'); + $this->assertInstanceOf(MojangUsername::class, $mojangUsername); + $this->assertGreaterThan($mojangUsernameFixture->last_pulled_at, $mojangUsername->last_pulled_at); + $this->assertLessThanOrEqual(time(), $mojangUsername->last_pulled_at); + } + + public function testExecuteChangedUsernameNotExists() { $expectedResponse = new UsernameToUUIDResponse(); $expectedResponse->id = '607153852b8c4909811f507ed8ee737f'; $expectedResponse->name = 'Chest'; $this->expectedResponse = $expectedResponse; - /** @var \common\models\Account $accountInfo */ - $accountInfo = $this->tester->grabFixture('accounts', 'admin'); - $body = new UsernameChanged([ - 'accountId' => $accountInfo['id'], - 'oldUsername' => $accountInfo['username'], - 'newUsername' => 'Chest', - ]); - $this->controller->routeUsernameChanged($body); + $this->task->username = 'Chest'; + $this->task->execute(mock(Queue::class)); /** @var MojangUsername|null $mojangUsername */ $mojangUsername = MojangUsername::findOne('Chest'); $this->assertInstanceOf(MojangUsername::class, $mojangUsername); } - public function testRouteUsernameChangedRemoveIfExistsNoMore() { + public function testExecuteRemoveIfExistsNoMore() { $this->expectedResponse = false; - /** @var \common\models\Account $accountInfo */ - $accountInfo = $this->tester->grabFixture('accounts', 'admin'); $username = $this->tester->grabFixture('mojangUsernames', 'not-exists')['username']; - $body = new UsernameChanged([ - 'accountId' => $accountInfo['id'], - 'oldUsername' => $accountInfo['username'], - 'newUsername' => $username, - ]); - $this->controller->routeUsernameChanged($body); + $this->task->username = $username; + $this->task->execute(mock(Queue::class)); /** @var MojangUsername|null $mojangUsername */ $mojangUsername = MojangUsername::findOne($username); $this->assertNull($mojangUsername); } - public function testRouteUsernameChangedUuidUpdated() { + public function testExecuteUuidUpdated() { $expectedResponse = new UsernameToUUIDResponse(); $expectedResponse->id = 'f498513ce8c84773be26ecfc7ed5185d'; $expectedResponse->name = 'jeb'; $this->expectedResponse = $expectedResponse; - /** @var \common\models\Account $accountInfo */ - $accountInfo = $this->tester->grabFixture('accounts', 'admin'); /** @var MojangUsername $mojangInfo */ $mojangInfo = $this->tester->grabFixture('mojangUsernames', 'uuid-changed'); $username = $mojangInfo['username']; - $body = new UsernameChanged([ - 'accountId' => $accountInfo['id'], - 'oldUsername' => $accountInfo['username'], - 'newUsername' => $username, - ]); - $this->controller->routeUsernameChanged($body); + $this->task->username = $username; + $this->task->execute(mock(Queue::class)); /** @var MojangUsername|null $mojangUsername */ $mojangUsername = MojangUsername::findOne($username); $this->assertInstanceOf(MojangUsername::class, $mojangUsername); $this->assertNotEquals($mojangInfo->uuid, $mojangUsername->uuid); } - public function testRouteAccountBanned() { - /** @var \common\models\Account $bannedAccount */ - $bannedAccount = $this->tester->grabFixture('accounts', 'banned-account'); - $this->tester->haveFixtures([ - 'oauthSessions' => \tests\codeception\common\fixtures\OauthSessionFixture::class, - 'minecraftAccessKeys' => \tests\codeception\common\fixtures\MinecraftAccessKeyFixture::class, - 'authSessions' => \tests\codeception\common\fixtures\AccountSessionFixture::class, - ]); - - $body = new AccountBanned(); - $body->accountId = $bannedAccount->id; - - $this->controller->routeAccountBanned($body); - $this->assertEmpty($bannedAccount->sessions); - $this->assertEmpty($bannedAccount->minecraftAccessKeys); - $this->assertEmpty($bannedAccount->oauthSessions); - } - } diff --git a/tests/codeception/config/config.php b/tests/codeception/config/config.php index 78b097c..72c5fa3 100644 --- a/tests/codeception/config/config.php +++ b/tests/codeception/config/config.php @@ -20,9 +20,6 @@ return [ // Для тестов нам не сильно важна безопасность, а вот время прохождения тестов значительно сокращается 'passwordHashCost' => 4, ], - 'amqp' => [ - 'class' => tests\codeception\common\_support\amqp\TestComponent::class, - ], 'queue' => [ 'class' => tests\codeception\common\_support\queue\Queue::class, ], From e3ade6a0cbf5f6663300a394616ba4eb726e2464 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sun, 8 Jul 2018 18:38:47 +0300 Subject: [PATCH 09/19] Update outdated dependencies --- composer.json | 8 +- composer.lock | 553 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 336 insertions(+), 225 deletions(-) diff --git a/composer.json b/composer.json index 8afc205..9bf9e12 100644 --- a/composer.json +++ b/composer.json @@ -6,8 +6,7 @@ "minimum-stability": "stable", "require": { "php": "^7.1", - "roave/security-advisories": "dev-master", - "yiisoft/yii2": "2.0.14", + "yiisoft/yii2": "2.0.15.1", "yiisoft/yii2-swiftmailer": "~2.1.0", "ramsey/uuid": "^3.5", "league/oauth2-server": "^4.1", @@ -28,7 +27,7 @@ "require-dev": { "yiisoft/yii2-debug": "*", "yiisoft/yii2-faker": "*", - "flow/jsonpath": "^0.3.1", + "flow/jsonpath": "^0.4.0", "phpunit/phpunit": "^6.0", "codeception/codeception": "2.3.8", "codeception/specify": "^1.0.0", @@ -37,7 +36,8 @@ "php-mock/php-mock-mockery": "^1.2.0", "friendsofphp/php-cs-fixer": "^2.11", "ely/php-code-style": "^0.1.0", - "predis/predis": "^1.1" + "predis/predis": "^1.1", + "roave/security-advisories": "dev-master" }, "repositories": [ { diff --git a/composer.lock b/composer.lock index 44bd2a0..60f1fee 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b576f6f9babd8e00a7fa6768d56c37e9", + "content-hash": "4334ca4dd8b377a9c40afd844527a850", "packages": [ { "name": "bacon/bacon-qr-code", @@ -508,16 +508,16 @@ }, { "name": "domnikl/statsd", - "version": "2.6.0", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/domnikl/statsd-php.git", - "reference": "ca9daa049fd9f353c0551384612bb4f17615b14a" + "reference": "529578b05e455280fbb2748bb0080026d86452bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/domnikl/statsd-php/zipball/ca9daa049fd9f353c0551384612bb4f17615b14a", - "reference": "ca9daa049fd9f353c0551384612bb4f17615b14a", + "url": "https://api.github.com/repos/domnikl/statsd-php/zipball/529578b05e455280fbb2748bb0080026d86452bb", + "reference": "529578b05e455280fbb2748bb0080026d86452bb", "shasum": "" }, "require": { @@ -551,7 +551,7 @@ "statsd", "udp" ], - "time": "2017-07-30T17:40:00+00:00" + "time": "2018-04-29T18:04:03+00:00" }, { "name": "egulias/email-validator", @@ -909,16 +909,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "6.3.0", + "version": "6.3.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699" + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699", - "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", "shasum": "" }, "require": { @@ -928,7 +928,7 @@ }, "require-dev": { "ext-curl": "*", - "phpunit/phpunit": "^4.0 || ^5.0", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", "psr/log": "^1.0" }, "suggest": { @@ -937,7 +937,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.2-dev" + "dev-master": "6.3-dev" } }, "autoload": { @@ -970,7 +970,7 @@ "rest", "web service" ], - "time": "2017-06-22T18:50:49+00:00" + "time": "2018-04-22T15:46:56+00:00" }, { "name": "guzzlehttp/promises", @@ -1364,24 +1364,24 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v2.2.1", + "version": "v2.2.2", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "7c74c5d08761ead7b5e89d07c854bc28eb0b2186" + "reference": "eccf915f45f911bfb189d1d1638d940ec6ee6e33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/7c74c5d08761ead7b5e89d07c854bc28eb0b2186", - "reference": "7c74c5d08761ead7b5e89d07c854bc28eb0b2186", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/eccf915f45f911bfb189d1d1638d940ec6ee6e33", + "reference": "eccf915f45f911bfb189d1d1638d940ec6ee6e33", "shasum": "" }, "require": { "php": "^7" }, "require-dev": { - "phpunit/phpunit": "^6", - "vimeo/psalm": "^0.3|^1" + "phpunit/phpunit": "^6|^7", + "vimeo/psalm": "^1" }, "type": "library", "autoload": { @@ -1422,7 +1422,7 @@ "hex2bin", "rfc4648" ], - "time": "2018-01-23T00:54:57+00:00" + "time": "2018-03-10T19:47:49+00:00" }, { "name": "paragonie/random_compat", @@ -1602,153 +1602,6 @@ ], "time": "2018-01-20T00:28:24+00:00" }, - { - "name": "roave/security-advisories", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "94230db36bded9d164ffccabcb38c67eedd63595" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/94230db36bded9d164ffccabcb38c67eedd63595", - "reference": "94230db36bded9d164ffccabcb38c67eedd63595", - "shasum": "" - }, - "conflict": { - "adodb/adodb-php": "<5.20.6", - "amphp/artax": "<1.0.6|>=2,<2.0.6", - "aws/aws-sdk-php": ">=3,<3.2.1", - "bugsnag/bugsnag-laravel": ">=2,<2.0.2", - "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.0.15|>=3.1,<3.1.4", - "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", - "cartalyst/sentry": "<=2.1.6", - "codeigniter/framework": "<=3.0.6", - "composer/composer": "<=1.0.0-alpha11", - "contao-components/mediaelement": ">=2.14.2,<2.21.1", - "contao/core": ">=2,<3.5.32", - "contao/core-bundle": ">=4,<4.4.8", - "contao/listing-bundle": ">=4,<4.4.8", - "contao/newsletter-bundle": ">=4,<4.1", - "doctrine/annotations": ">=1,<1.2.7", - "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", - "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1", - "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2", - "doctrine/doctrine-bundle": "<1.5.2", - "doctrine/doctrine-module": "<=0.7.1", - "doctrine/mongodb-odm": ">=1,<1.0.2", - "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", - "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1", - "dompdf/dompdf": ">=0.6,<0.6.2", - "drupal/core": ">=8,<8.3.7", - "drupal/drupal": ">=8,<8.3.7", - "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.2|>=5.4,<5.4.10.1|>=2017.8,<2017.8.1.1", - "firebase/php-jwt": "<2", - "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", - "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", - "gree/jose": "<=2.2", - "gregwar/rst": "<1.0.3", - "guzzlehttp/guzzle": ">=6,<6.2.1|>=4.0.0-rc2,<4.2.4|>=5,<5.3.1", - "illuminate/auth": ">=4,<4.0.99|>=4.1,<4.1.26", - "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29", - "joomla/session": "<1.3.1", - "laravel/framework": ">=4,<4.0.99|>=4.1,<4.1.29", - "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", - "magento/magento1ce": ">=1.5.0.1,<1.9.3.2", - "magento/magento1ee": ">=1.9,<1.14.3.2", - "magento/magento2ce": ">=2,<2.2", - "monolog/monolog": ">=1.8,<1.12", - "namshi/jose": "<2.2", - "onelogin/php-saml": "<2.10.4", - "oro/crm": ">=1.7,<1.7.4", - "oro/platform": ">=1.7,<1.7.4", - "padraic/humbug_get_contents": "<1.1.2", - "phpmailer/phpmailer": ">=5,<5.2.24", - "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", - "phpxmlrpc/extras": "<0.6.1", - "pusher/pusher-php-server": "<2.2.1", - "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", - "shopware/shopware": "<5.3.7", - "silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11", - "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", - "silverstripe/framework": ">=3,<3.3", - "silverstripe/userforms": "<3", - "simplesamlphp/saml2": "<1.10.4|>=2,<2.3.5|>=3,<3.1.1", - "simplesamlphp/simplesamlphp": "<1.15.2", - "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", - "socalnick/scn-social-auth": "<1.15.2", - "squizlabs/php_codesniffer": ">=1,<2.8.1", - "swiftmailer/swiftmailer": ">=4,<5.4.5", - "symfony/dependency-injection": ">=2,<2.0.17", - "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2", - "symfony/http-foundation": ">=2,<2.3.27|>=2.4,<2.5.11|>=2.6,<2.6.6", - "symfony/http-kernel": ">=2,<2.3.29|>=2.4,<2.5.12|>=2.6,<2.6.8", - "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/routing": ">=2,<2.0.19", - "symfony/security": ">=2,<2.0.25|>=2.1,<2.1.13|>=2.2,<2.2.9|>=2.3,<2.3.37|>=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8.23,<2.8.25|>=3.2.10,<3.2.12|>=3.3.3,<3.3.5", - "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.6|>=2.8.23,<2.8.25|>=3,<3.0.6|>=3.2.10,<3.2.12|>=3.3.3,<3.3.5", - "symfony/security-csrf": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/serializer": ">=2,<2.0.11", - "symfony/symfony": ">=2,<2.3.41|>=2.4,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/translation": ">=2,<2.0.17", - "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", - "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", - "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7", - "thelia/backoffice-default-template": ">=2.1,<2.1.2", - "thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2", - "twig/twig": "<1.20", - "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.22|>=8,<8.7.5", - "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5", - "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4", - "willdurand/js-translation-bundle": "<2.1.1", - "yiisoft/yii": ">=1.1.14,<1.1.15", - "yiisoft/yii2": "<2.0.14", - "yiisoft/yii2-bootstrap": "<2.0.4", - "yiisoft/yii2-dev": "<2.0.14", - "yiisoft/yii2-gii": "<2.0.4", - "yiisoft/yii2-jui": "<2.0.4", - "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", - "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2", - "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", - "zendframework/zend-db": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.10|>=2.3,<2.3.5", - "zendframework/zend-diactoros": ">=1,<1.0.4", - "zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-http": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.3,<2.3.8|>=2.4,<2.4.1", - "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6", - "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3", - "zendframework/zend-mail": ">=2,<2.4.11|>=2.5,<2.7.2", - "zendframework/zend-navigation": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-session": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.9|>=2.3,<2.3.4", - "zendframework/zend-validator": ">=2.3,<2.3.6", - "zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6", - "zendframework/zendframework": ">=2,<2.4.11|>=2.5,<2.5.1", - "zendframework/zendframework1": "<1.12.20", - "zendframework/zendopenid": ">=2,<2.0.2", - "zendframework/zendxml": ">=1,<1.0.1", - "zetacomponents/mail": "<1.8.2", - "zf-commons/zfc-user": "<1.2.2", - "zfcampus/zf-apigility-doctrine": ">=1,<1.0.3", - "zfr/zfr-oauth2-server-module": "<0.1.2" - }, - "type": "metapackage", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "role": "maintainer" - } - ], - "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", - "time": "2018-02-19T09:31:21+00:00" - }, { "name": "sentry/sentry", "version": "1.8.3", @@ -1815,16 +1668,16 @@ }, { "name": "spomky-labs/otphp", - "version": "v9.0.3", + "version": "v9.1.0", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/otphp.git", - "reference": "26d19c0baff675fb64193e22b777aa9be63b8deb" + "reference": "4b303e33972f6c886f32b38fea60f41be6d74ec1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/otphp/zipball/26d19c0baff675fb64193e22b777aa9be63b8deb", - "reference": "26d19c0baff675fb64193e22b777aa9be63b8deb", + "url": "https://api.github.com/repos/Spomky-Labs/otphp/zipball/4b303e33972f6c886f32b38fea60f41be6d74ec1", + "reference": "4b303e33972f6c886f32b38fea60f41be6d74ec1", "shasum": "" }, "require": { @@ -1872,7 +1725,7 @@ "otp", "totp" ], - "time": "2017-11-23T08:44:21+00:00" + "time": "2018-02-26T13:45:15+00:00" }, { "name": "swiftmailer/swiftmailer", @@ -1931,16 +1784,16 @@ }, { "name": "symfony/http-foundation", - "version": "v3.4.4", + "version": "v3.4.12", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "8c39071ac9cc7e6d8dab1d556c990dc0d2cc3d30" + "reference": "1c28679fcbb0d9b35e4fd49fbb74d2ca4ea17bce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/8c39071ac9cc7e6d8dab1d556c990dc0d2cc3d30", - "reference": "8c39071ac9cc7e6d8dab1d556c990dc0d2cc3d30", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/1c28679fcbb0d9b35e4fd49fbb74d2ca4ea17bce", + "reference": "1c28679fcbb0d9b35e4fd49fbb74d2ca4ea17bce", "shasum": "" }, "require": { @@ -1981,7 +1834,7 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2018-01-29T09:03:43+00:00" + "time": "2018-06-21T11:10:19+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -2202,16 +2055,16 @@ }, { "name": "yiisoft/yii2", - "version": "2.0.14", + "version": "2.0.15.1", "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-framework.git", - "reference": "1c9cf916b1394681c7d043e79e1522c33e5bc6c1" + "reference": "ed3a9e1c4abe206e1c3ce48a6b3624119b79850d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/1c9cf916b1394681c7d043e79e1522c33e5bc6c1", - "reference": "1c9cf916b1394681c7d043e79e1522c33e5bc6c1", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/ed3a9e1c4abe206e1c3ce48a6b3624119b79850d", + "reference": "ed3a9e1c4abe206e1c3ce48a6b3624119b79850d", "shasum": "" }, "require": { @@ -2298,7 +2151,7 @@ "framework", "yii2" ], - "time": "2018-02-18T22:52:12+00:00" + "time": "2018-03-21T18:36:53+00:00" }, { "name": "yiisoft/yii2-composer", @@ -2436,20 +2289,23 @@ }, { "name": "yiisoft/yii2-redis", - "version": "2.0.7", + "version": "2.0.8", "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-redis.git", - "reference": "3891bb19f3ddc7ad744b439fe1d656ebb5b60a99" + "reference": "ffe6bff8dc6be4bb84c9495cd3ef7ef1161c1314" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-redis/zipball/3891bb19f3ddc7ad744b439fe1d656ebb5b60a99", - "reference": "3891bb19f3ddc7ad744b439fe1d656ebb5b60a99", + "url": "https://api.github.com/repos/yiisoft/yii2-redis/zipball/ffe6bff8dc6be4bb84c9495cd3ef7ef1161c1314", + "reference": "ffe6bff8dc6be4bb84c9495cd3ef7ef1161c1314", "shasum": "" }, "require": { - "yiisoft/yii2": "~2.0.13" + "yiisoft/yii2": "~2.0.14" + }, + "require-dev": { + "yiisoft/yii2-dev": "~2.0.14" }, "type": "yii2-extension", "extra": { @@ -2459,7 +2315,7 @@ }, "autoload": { "psr-4": { - "yii\\redis\\": "" + "yii\\redis\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2480,20 +2336,20 @@ "session", "yii2" ], - "time": "2017-12-11T21:17:34+00:00" + "time": "2018-03-20T11:01:04+00:00" }, { "name": "yiisoft/yii2-swiftmailer", - "version": "2.1.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-swiftmailer.git", - "reference": "563570c9aa19ca47c1b22e3032983229378e9274" + "reference": "fd917fbe63b7ea796c52902143b83b98e65bfb73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-swiftmailer/zipball/563570c9aa19ca47c1b22e3032983229378e9274", - "reference": "563570c9aa19ca47c1b22e3032983229378e9274", + "url": "https://api.github.com/repos/yiisoft/yii2-swiftmailer/zipball/fd917fbe63b7ea796c52902143b83b98e65bfb73", + "reference": "fd917fbe63b7ea796c52902143b83b98e65bfb73", "shasum": "" }, "require": { @@ -2503,12 +2359,12 @@ "type": "yii2-extension", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "2.1.x-dev" } }, "autoload": { "psr-4": { - "yii\\swiftmailer\\": "" + "yii\\swiftmailer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2530,7 +2386,7 @@ "swiftmailer", "yii2" ], - "time": "2017-08-04T10:48:17+00:00" + "time": "2018-04-24T23:17:42+00:00" } ], "packages-dev": [ @@ -2882,6 +2738,50 @@ ], "time": "2016-08-30T16:08:34+00:00" }, + { + "name": "composer/xdebug-handler", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "c919dc6c62e221fc6406f861ea13433c0aa24f08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/c919dc6c62e221fc6406f861ea13433c0aa24f08", + "reference": "c919dc6c62e221fc6406f861ea13433c0aa24f08", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0", + "psr/log": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "time": "2018-04-11T15:42:36+00:00" + }, { "name": "doctrine/instantiator", "version": "1.1.0", @@ -3042,16 +2942,16 @@ }, { "name": "flow/jsonpath", - "version": "0.3.4", + "version": "0.4.0", "source": { "type": "git", "url": "https://github.com/FlowCommunications/JSONPath.git", - "reference": "00aa9c361e4d0a210dd95f3c917a1e0dde3a957f" + "reference": "f0222818d5c938e4ab668ab2e2c079bd51a27112" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FlowCommunications/JSONPath/zipball/00aa9c361e4d0a210dd95f3c917a1e0dde3a957f", - "reference": "00aa9c361e4d0a210dd95f3c917a1e0dde3a957f", + "url": "https://api.github.com/repos/FlowCommunications/JSONPath/zipball/f0222818d5c938e4ab668ab2e2c079bd51a27112", + "reference": "f0222818d5c938e4ab668ab2e2c079bd51a27112", "shasum": "" }, "require": { @@ -3079,24 +2979,25 @@ } ], "description": "JSONPath implementation for parsing, searching and flattening arrays", - "time": "2016-09-06T17:43:18+00:00" + "time": "2018-03-04T16:39:47+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.11.1", + "version": "v2.12.2", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "ad94441c17b8ef096e517acccdbf3238af8a2da8" + "reference": "dcc87d5414e9d0bd316fce81a5bedb9ce720b183" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/ad94441c17b8ef096e517acccdbf3238af8a2da8", - "reference": "ad94441c17b8ef096e517acccdbf3238af8a2da8", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/dcc87d5414e9d0bd316fce81a5bedb9ce720b183", + "reference": "dcc87d5414e9d0bd316fce81a5bedb9ce720b183", "shasum": "" }, "require": { "composer/semver": "^1.4", + "composer/xdebug-handler": "^1.0", "doctrine/annotations": "^1.2", "ext-json": "*", "ext-tokenizer": "*", @@ -3118,27 +3019,26 @@ "require-dev": { "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", "justinrainbow/json-schema": "^5.0", - "keradus/cli-executor": "^1.0", + "keradus/cli-executor": "^1.1", "mikey179/vfsstream": "^1.6", - "php-coveralls/php-coveralls": "^2.0", + "php-coveralls/php-coveralls": "^2.1", "php-cs-fixer/accessible-object": "^1.0", - "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", - "phpunitgoodpractices/traits": "^1.3.1", - "symfony/phpunit-bridge": "^3.2.2 || ^4.0" + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.0.1", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.0.1", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1", + "phpunitgoodpractices/traits": "^1.5.1", + "symfony/phpunit-bridge": "^4.0" }, "suggest": { "ext-mbstring": "For handling non-UTF8 characters in cache signature.", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "For IsIdenticalString constraint.", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "For XmlMatchesXsd constraint.", "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." }, "bin": [ "php-cs-fixer" ], "type": "application", - "extra": { - "branch-alias": { - "dev-master": "2.11-dev" - } - }, "autoload": { "psr-4": { "PhpCsFixer\\": "src/" @@ -3148,9 +3048,6 @@ "tests/Test/AbstractIntegrationCaseFactory.php", "tests/Test/AbstractIntegrationTestCase.php", "tests/Test/Assert/AssertTokensTrait.php", - "tests/Test/Constraint/SameStringsConstraint.php", - "tests/Test/Constraint/SameStringsConstraintForV5.php", - "tests/Test/Constraint/SameStringsConstraintForV7.php", "tests/Test/IntegrationCase.php", "tests/Test/IntegrationCaseFactory.php", "tests/Test/IntegrationCaseFactoryInterface.php", @@ -3173,7 +3070,7 @@ } ], "description": "A tool to automatically fix PHP code style", - "time": "2018-03-21T17:41:26+00:00" + "time": "2018-07-06T10:37:40+00:00" }, { "name": "fzaninotto/faker", @@ -4362,6 +4259,220 @@ ], "time": "2016-06-16T16:22:20+00:00" }, + { + "name": "psr/log", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10T12:19:37+00:00" + }, + { + "name": "roave/security-advisories", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Roave/SecurityAdvisories.git", + "reference": "0253937ef2720f45fbe421e3ba486587b3080c35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/0253937ef2720f45fbe421e3ba486587b3080c35", + "reference": "0253937ef2720f45fbe421e3ba486587b3080c35", + "shasum": "" + }, + "conflict": { + "3f/pygmentize": "<1.2", + "adodb/adodb-php": "<5.20.12", + "amphp/artax": "<1.0.6|>=2,<2.0.6", + "amphp/http": "<1.0.1", + "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", + "aws/aws-sdk-php": ">=3,<3.2.1", + "bugsnag/bugsnag-laravel": ">=2,<2.0.2", + "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.0.15|>=3.1,<3.1.4|>=3.4,<3.4.14|>=3.5,<3.5.17|>=3.6,<3.6.4", + "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", + "cartalyst/sentry": "<=2.1.6", + "codeigniter/framework": "<=3.0.6", + "composer/composer": "<=1.0.0-alpha11", + "contao-components/mediaelement": ">=2.14.2,<2.21.1", + "contao/core": ">=2,<3.5.35", + "contao/core-bundle": ">=4,<4.4.18|>=4.5,<4.5.8", + "contao/listing-bundle": ">=4,<4.4.8", + "contao/newsletter-bundle": ">=4,<4.1", + "doctrine/annotations": ">=1,<1.2.7", + "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", + "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1", + "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2", + "doctrine/doctrine-bundle": "<1.5.2", + "doctrine/doctrine-module": "<=0.7.1", + "doctrine/mongodb-odm": ">=1,<1.0.2", + "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", + "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1", + "dompdf/dompdf": ">=0.6,<0.6.2", + "drupal/core": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3", + "drupal/drupal": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3", + "erusev/parsedown": "<1.7", + "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.3|>=5.4,<5.4.11.3|>=2017.8,<2017.8.1.1|>=2017.12,<2017.12.2.1", + "firebase/php-jwt": "<2", + "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", + "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", + "fuel/core": "<1.8.1", + "gree/jose": "<=2.2", + "gregwar/rst": "<1.0.3", + "guzzlehttp/guzzle": ">=6,<6.2.1|>=4.0.0-rc2,<4.2.4|>=5,<5.3.1", + "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", + "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29", + "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", + "joomla/session": "<1.3.1", + "kreait/firebase-php": ">=3.2,<3.8.1", + "laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", + "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", + "magento/magento1ce": ">=1.5.0.1,<1.9.3.2", + "magento/magento1ee": ">=1.9,<1.14.3.2", + "magento/product-community-edition": ">=2,<2.2.5", + "monolog/monolog": ">=1.8,<1.12", + "namshi/jose": "<2.2", + "onelogin/php-saml": "<2.10.4", + "oro/crm": ">=1.7,<1.7.4", + "oro/platform": ">=1.7,<1.7.4", + "padraic/humbug_get_contents": "<1.1.2", + "pagarme/pagarme-php": ">=0,<3", + "paragonie/random_compat": "<2", + "paypal/merchant-sdk-php": "<3.12", + "phpmailer/phpmailer": ">=5,<5.2.24", + "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", + "phpxmlrpc/extras": "<0.6.1", + "propel/propel": ">=2.0.0-alpha1,<=2.0.0-alpha7", + "propel/propel1": ">=1,<=1.7.1", + "pusher/pusher-php-server": "<2.2.1", + "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", + "sensiolabs/connect": "<4.2.3", + "shopware/shopware": "<5.3.7", + "silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11", + "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", + "silverstripe/framework": ">=3,<3.3", + "silverstripe/userforms": "<3", + "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4", + "simplesamlphp/simplesamlphp": "<1.15.2", + "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", + "slim/slim": "<2.6", + "socalnick/scn-social-auth": "<1.15.2", + "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", + "stormpath/sdk": ">=0,<9.9.99", + "swiftmailer/swiftmailer": ">=4,<5.4.5", + "symfony/dependency-injection": ">=2,<2.0.17", + "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", + "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2", + "symfony/http-foundation": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/http-kernel": ">=2,<2.3.29|>=2.4,<2.5.12|>=2.6,<2.6.8", + "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", + "symfony/routing": ">=2,<2.0.19", + "symfony/security": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-bundle": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.37|>=3,<3.3.17|>=3.4,<3.4.7|>=4,<4.0.7", + "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-guard": ">=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/serializer": ">=2,<2.0.11", + "symfony/symfony": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/translation": ">=2,<2.0.17", + "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", + "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", + "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7", + "thelia/backoffice-default-template": ">=2.1,<2.1.2", + "thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2", + "titon/framework": ">=0,<9.9.99", + "twig/twig": "<1.20", + "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.22|>=8,<8.7.5", + "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5", + "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4", + "willdurand/js-translation-bundle": "<2.1.1", + "yiisoft/yii": ">=1.1.14,<1.1.15", + "yiisoft/yii2": "<2.0.15", + "yiisoft/yii2-bootstrap": "<2.0.4", + "yiisoft/yii2-dev": "<2.0.15", + "yiisoft/yii2-elasticsearch": "<2.0.5", + "yiisoft/yii2-gii": "<2.0.4", + "yiisoft/yii2-jui": "<2.0.4", + "yiisoft/yii2-redis": "<2.0.8", + "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", + "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2", + "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", + "zendframework/zend-db": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.10|>=2.3,<2.3.5", + "zendframework/zend-diactoros": ">=1,<1.0.4", + "zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-http": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.3,<2.3.8|>=2.4,<2.4.1", + "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6", + "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3", + "zendframework/zend-mail": ">=2,<2.4.11|>=2.5,<2.7.2", + "zendframework/zend-navigation": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-session": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.9|>=2.3,<2.3.4", + "zendframework/zend-validator": ">=2.3,<2.3.6", + "zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6", + "zendframework/zendframework": ">=2,<2.4.11|>=2.5,<2.5.1", + "zendframework/zendframework1": "<1.12.20", + "zendframework/zendopenid": ">=2,<2.0.2", + "zendframework/zendxml": ">=1,<1.0.1", + "zetacomponents/mail": "<1.8.2", + "zf-commons/zfc-user": "<1.2.2", + "zfcampus/zf-apigility-doctrine": ">=1,<1.0.3", + "zfr/zfr-oauth2-server-module": "<0.1.2" + }, + "type": "metapackage", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "role": "maintainer" + } + ], + "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", + "time": "2018-07-04T05:48:21+00:00" + }, { "name": "sebastian/code-unit-reverse-lookup", "version": "1.0.1", @@ -5730,8 +5841,8 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { - "roave/security-advisories": 20, - "ely/email-renderer": 20 + "ely/email-renderer": 20, + "roave/security-advisories": 20 }, "prefer-stable": false, "prefer-lowest": false, From 7f42aadfcfd987870a31f3f0f5479be0b6b5e431 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sun, 8 Jul 2018 18:55:53 +0300 Subject: [PATCH 10/19] Fix code style --- common/tasks/DeliveryWebHook.php | 1 + console/controllers/WebhooksController.php | 2 +- tests/codeception/common/unit/tasks/DeliveryWebHookTest.php | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/common/tasks/DeliveryWebHook.php b/common/tasks/DeliveryWebHook.php index 7561d2a..a3d639b 100644 --- a/common/tasks/DeliveryWebHook.php +++ b/common/tasks/DeliveryWebHook.php @@ -78,6 +78,7 @@ class DeliveryWebHook implements RetryableJobInterface { ]); } catch (ClientException $e) { Yii::info("Delivery for {$this->url} has failed with {$e->getResponse()->getStatusCode()} status."); + return; } } diff --git a/console/controllers/WebhooksController.php b/console/controllers/WebhooksController.php index 6f77acc..1dcd731 100644 --- a/console/controllers/WebhooksController.php +++ b/console/controllers/WebhooksController.php @@ -38,7 +38,7 @@ class WebhooksController extends Controller { if ($eventIndex !== '') { $events[] = $options[$eventIndex]; } - } while($eventIndex !== '' || empty($events)); // User must choose at least one event + } while ($eventIndex !== '' || empty($events)); $form->url = $url; $form->events = $events; diff --git a/tests/codeception/common/unit/tasks/DeliveryWebHookTest.php b/tests/codeception/common/unit/tasks/DeliveryWebHookTest.php index af74384..f798b60 100644 --- a/tests/codeception/common/unit/tasks/DeliveryWebHookTest.php +++ b/tests/codeception/common/unit/tasks/DeliveryWebHookTest.php @@ -109,7 +109,8 @@ class DeliveryWebHookTest extends TestCase { private function createMockedTask(): DeliveryWebHook { $container = &$this->historyContainer; $response = $this->response; - return new class ($container, $response) extends DeliveryWebHook { + + return new class($container, $response) extends DeliveryWebHook { private $historyContainer; private $response; From a0d88e8a8fd2f4509e303ef3d8c43399fb3df866 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sun, 8 Jul 2018 22:21:33 +0300 Subject: [PATCH 11/19] Allow any valid locale for account lang --- common/config/bootstrap.php | 5 +- common/validators/LanguageValidator.php | 38 ++++++----- .../m180708_155425_extends_locale_field.php | 15 +++++ .../codeception/common/unit/fixtures/.gitkeep | 0 .../common/unit/fixtures/data/i18n/en.json | 3 - .../common/unit/fixtures/data/i18n/ru.json | 3 - .../unit/validators/LanguageValidatorTest.php | 63 +++++++++++-------- 7 files changed, 72 insertions(+), 55 deletions(-) create mode 100644 console/migrations/m180708_155425_extends_locale_field.php create mode 100644 tests/codeception/common/unit/fixtures/.gitkeep delete mode 100644 tests/codeception/common/unit/fixtures/data/i18n/en.json delete mode 100644 tests/codeception/common/unit/fixtures/data/i18n/ru.json diff --git a/common/config/bootstrap.php b/common/config/bootstrap.php index 64528ff..bce9d52 100644 --- a/common/config/bootstrap.php +++ b/common/config/bootstrap.php @@ -1,5 +1,4 @@ getFilesNames(); - if (in_array($value, $files)) { - return null; + $primary = Locale::getPrimaryLanguage($value); + $region = Locale::getRegion($value); + $locales = ResourceBundle::getLocales(''); // http://php.net/manual/ru/resourcebundle.locales.php#115965 + if (($region !== '' && strtolower($primary) !== strtolower($region)) && !in_array($value, $locales)) { + return [$this->message, []]; } - return [$this->message, []]; - } - - protected function getFilesNames() { - $files = array_values(array_filter(scandir($this->getFolderPath()), function(&$value) { - return $value !== '..' && $value !== '.'; - })); - - return array_map(function($value) { - return basename($value, '.json'); - }, $files); - } - - protected function getFolderPath() { - return Yii::getAlias('@frontend/src/i18n'); + return null; } } diff --git a/console/migrations/m180708_155425_extends_locale_field.php b/console/migrations/m180708_155425_extends_locale_field.php new file mode 100644 index 0000000..600442b --- /dev/null +++ b/console/migrations/m180708_155425_extends_locale_field.php @@ -0,0 +1,15 @@ +alterColumn('{{%accounts}}', 'lang', $this->string()->notNull()->defaultValue('en')); + } + + public function safeDown() { + $this->alterColumn('{{%accounts}}', 'lang', $this->string(5)->notNull()->defaultValue('en')); + } + +} diff --git a/tests/codeception/common/unit/fixtures/.gitkeep b/tests/codeception/common/unit/fixtures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/codeception/common/unit/fixtures/data/i18n/en.json b/tests/codeception/common/unit/fixtures/data/i18n/en.json deleted file mode 100644 index cacf5c2..0000000 --- a/tests/codeception/common/unit/fixtures/data/i18n/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "testString": "testValue" -} diff --git a/tests/codeception/common/unit/fixtures/data/i18n/ru.json b/tests/codeception/common/unit/fixtures/data/i18n/ru.json deleted file mode 100644 index 9734d39..0000000 --- a/tests/codeception/common/unit/fixtures/data/i18n/ru.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "testString": "тестовоеЗначение" -} diff --git a/tests/codeception/common/unit/validators/LanguageValidatorTest.php b/tests/codeception/common/unit/validators/LanguageValidatorTest.php index e40e72c..e6d077e 100644 --- a/tests/codeception/common/unit/validators/LanguageValidatorTest.php +++ b/tests/codeception/common/unit/validators/LanguageValidatorTest.php @@ -1,39 +1,50 @@ createModelWithFixturePath(); - $this->assertEquals(['en', 'ru'], $this->callProtected($model, 'getFilesNames')); - } - - public function testValidateValueSupportedLanguage() { - $model = $this->createModelWithFixturePath(); - $this->assertNull($this->callProtected($model, 'validateValue', 'ru')); - } - - public function testValidateNotSupportedLanguage() { - $model = $this->createModelWithFixturePath(); - $this->assertEquals([$model->message, []], $this->callProtected($model, 'validateValue', 'by')); - } /** - * @return LanguageValidator + * @param string $locale + * @param bool $shouldBeValid + * + * @dataProvider getTestCases */ - private function createModelWithFixturePath() { - return new class extends LanguageValidator { - public function getFolderPath() { - return __DIR__ . '/../fixtures/data/i18n'; - } - }; + public function testValidate(string $locale, bool $shouldBeValid): void { + $validator = new LanguageValidator(); + $result = $validator->validate($locale, $error); + $this->assertSame($shouldBeValid, $result, $locale); + if (!$shouldBeValid) { + $this->assertSame($validator->message, $error); + } + } + + public function getTestCases(): array { + return [ + // valid + ['de', true], + ['de_DE', true], + ['deu', true], + ['en', true], + ['en_US', true], + ['fil', true], + ['fil_PH', true], + ['zh', true], + ['zh_Hans_CN', true], + ['zh_Hant_HK', true], + // invalid + ['de_FR', false], + ['fr_US', false], + ['foo_bar', false], + ['foo_bar_baz', false], + ]; } } From 49d612daa1c9993f494b6233a07bf47b93311ec4 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Tue, 10 Jul 2018 20:02:19 +0300 Subject: [PATCH 12/19] Replace debian-based image with alpine-based. Remove supervisor usage. Cron now runs as separate service. CI for backend now performs without Docker. --- .gitlab-ci.yml | 45 ++++-------- Dockerfile | 119 ++++++++++++++++++++------------ Dockerfile-dev | 52 -------------- composer.json | 2 +- composer.lock | 4 +- docker-compose.dev.yml | 33 +++++---- docker-compose.prod.yml | 9 +++ docker/php/bootstrap.sh | 41 ----------- docker/php/docker-entrypoint.sh | 62 +++++++++++++++++ docker/php/php.dev.ini | 5 ++ docker/php/php.prod.ini | 14 ++++ docker/{ => php}/wait-for-it.sh | 0 12 files changed, 201 insertions(+), 185 deletions(-) delete mode 100644 Dockerfile-dev delete mode 100755 docker/php/bootstrap.sh create mode 100755 docker/php/docker-entrypoint.sh create mode 100644 docker/php/php.dev.ini create mode 100644 docker/php/php.prod.ini rename docker/{ => php}/wait-for-it.sh (100%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8deb81b..3a5aacb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,50 +6,31 @@ stages: variables: CONTAINER_IMAGE: "registry.ely.by/elyby/accounts" -test:backend: - image: docker:18.02 +check backend codestyle: + image: edbizarro/gitlab-ci-pipeline-php:7.2-alpine + stage: test + script: + - vendor/bin/php-cs-fixer fix -v --dry-run + +test backend: + image: edbizarro/gitlab-ci-pipeline-php:7.2-alpine services: - mariadb:10.2.11 - redis:3.0-alpine variables: + # app config + YII_ENV: "test" + YII_DEBUG: "true" # mariadb config MYSQL_RANDOM_ROOT_PASSWORD: "true" MYSQL_DATABASE: "ely_accounts_test" MYSQL_USER: "ely_accounts_tester" MYSQL_PASSWORD: "ely_accounts_tester_password" stage: test - before_script: - - docker login -u gitlab-ci -p $CI_BUILD_TOKEN registry.ely.by - - echo "$SSH_PRIVATE_KEY" > id_rsa script: - - export TEMP_DEV_IMAGE="${CONTAINER_IMAGE}:ci-${CI_BUILD_ID}" - - docker build --pull -f Dockerfile-dev -t $TEMP_DEV_IMAGE . - - > - docker run --rm - $TEMP_DEV_IMAGE - bash -c " - rm /usr/local/etc/php/conf.d/xdebug.ini && - cp -r /var/www/vendor /var/www/html/vendor && - vendor/bin/php-cs-fixer fix -v --dry-run - " - - > - docker run --rm - --add-host=mariadb:`getent hosts mariadb | awk '{ print $1 ; exit }'` - --add-host=redis:`getent hosts redis | awk '{ print $1 ; exit }'` - -e YII_DEBUG="true" - -e YII_ENV="test" - -e DB_HOST="mariadb" - -e DB_DATABASE="ely_accounts_test" - -e DB_USER="ely_accounts_tester" - -e DB_PASSWORD="ely_accounts_tester_password" - -e REDIS_HOST="redis" - $TEMP_DEV_IMAGE - bash -c " - rm /usr/local/etc/php/conf.d/xdebug.ini && - docker-entrypoint.sh php vendor/bin/codecept run -c tests - " + - docker-entrypoint.sh vendor/bin/codecept run -c tests -test:frontend: +test frontend: image: node:9.2.1-alpine stage: test cache: diff --git a/Dockerfile b/Dockerfile index 54d8000..652ab21 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,55 +1,86 @@ -FROM registry.ely.by/elyby/accounts-php:1.8.0 +FROM node:9.11.2-alpine as frontend -# bootstrap скрипт для проекта -COPY docker/php/bootstrap.sh /bootstrap.sh -# Вносим конфигурации для крона и воркеров -COPY docker/cron/* /etc/cron.d/ -COPY docker/supervisor/* /etc/supervisor/conf.d/ +WORKDIR /app -COPY id_rsa /root/.ssh/id_rsa +COPY ./frontend/package.json ./ +COPY ./frontend/scripts ./scripts +COPY ./frontend/webpack-utils ./webpack-utils +COPY ./frontend/yarn.lock ./ +RUN yarn build:install -# Включаем поддержку ssh -RUN chmod 400 ~/.ssh/id_rsa \ - && eval $(ssh-agent -s) \ - && ssh-add /root/.ssh/id_rsa \ - && touch /root/.ssh/known_hosts \ - && ssh-keyscan github.com gitlab.ely.by >> /root/.ssh/known_hosts +COPY ./frontend . +RUN yarn build:quiet -# Копируем списки зависимостей composer в родительскую директорию, которая не будет синкаться -# с хостом через volume на dev окружении. В entrypoint эта папка будет скопирована обратно -COPY ./composer.json /var/www/composer.json -COPY ./composer.lock /var/www/composer.lock -# Устанавливаем зависимости PHP -RUN cd .. \ - && composer install --no-interaction --no-suggest --no-dev --optimize-autoloader \ - && cd - +FROM php:7.2.7-fpm-alpine3.7 -# Устанавливаем зависимости для Node.js -# Делаем это отдельно, чтобы можно было воспользоваться кэшем, если от предыдущего билда -# ничего не менялось в зависимостях -RUN mkdir -p /var/www/frontend +# bash needed to support wait-for-it script +RUN apk add --update --no-cache \ + git \ + bash \ + openssh \ + dcron \ + zlib-dev \ + icu-dev \ + libintl \ + imagemagick-dev \ + imagemagick \ + && docker-php-ext-install \ + zip \ + pdo_mysql \ + intl \ + pcntl \ + opcache \ + && apk add --no-cache --virtual ".phpize-deps" $PHPIZE_DEPS \ + && yes | pecl install xdebug-2.6.0 \ + && yes | pecl install imagick \ + && docker-php-ext-enable imagick \ + && apk del ".phpize-deps" \ + && rm -rf /usr/share/man \ + && rm -rf /tmp/* \ + && mkdir /etc/cron.d -COPY ./frontend/package.json /var/www/frontend/ -COPY ./frontend/yarn.lock /var/www/frontend/ -COPY ./frontend/scripts /var/www/frontend/scripts -COPY ./frontend/webpack-utils /var/www/frontend/webpack-utils +COPY --from=composer:1.6.5 /usr/bin/composer /usr/bin/composer +COPY --from=node:9.11.2-alpine /usr/local/bin/node /usr/bin/ +COPY --from=node:9.11.2-alpine /usr/lib/libgcc* /usr/lib/libstdc* /usr/lib/* /usr/lib/ -RUN cd /var/www/frontend \ - && yarn run build:install \ - && cd - +# ENV variables for composer +ENV COMPOSER_NO_INTERACTION 1 +ENV COMPOSER_ALLOW_SUPERUSER 1 -# Удаляем ключи из production контейнера на всякий случай -RUN rm -rf /root/.ssh +RUN mkdir /root/.composer \ + && echo '{"github-oauth": {"github.com": "***REMOVED***"}}' > ~/.composer/auth.json \ + && composer global require --no-progress "hirak/prestissimo:^0.3.7" \ + && composer clear-cache -# Наконец переносим все сорцы внутрь контейнера -COPY . /var/www/html +COPY ./docker/php/wait-for-it.sh /usr/local/bin/wait-for-it -# Билдим фронт -RUN cd frontend \ - && ln -s /var/www/frontend/node_modules $PWD/node_modules \ - && yarn run build:quiet \ - && rm node_modules \ - # Копируем билд наружу, чтобы его не затёрло volume в dev режиме - && cp -r ./dist /var/www/dist \ - && cd - +COPY ./composer.* /var/www/html/ + +ARG build_env=prod +ENV YII_ENV=$build_env + +RUN if [ "$build_env" = "prod" ] ; then \ + composer install --no-interaction --no-suggest --no-dev --optimize-autoloader; \ + else \ + composer install --no-interaction --no-suggest; \ + fi \ + && composer clear-cache + +COPY ./docker/php/*.ini /usr/local/etc/php/conf.d/ +COPY ./docker/php/docker-entrypoint.sh /usr/local/bin/ +COPY ./docker/cron/* /etc/cron.d/ + +COPY --from=frontend /app/dist /var/www/html/frontend/dist + +COPY ./api /var/www/html/api/ +COPY ./common /var/www/html/common/ +COPY ./console /var/www/html/console/ +COPY ./yii /var/www/html/yii + +# Expose everything under /var/www/html to share it with nginx +VOLUME ["/var/www/html"] + +WORKDIR /var/www/html +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["php-fpm"] diff --git a/Dockerfile-dev b/Dockerfile-dev deleted file mode 100644 index dec0312..0000000 --- a/Dockerfile-dev +++ /dev/null @@ -1,52 +0,0 @@ -FROM registry.ely.by/elyby/accounts-php:1.8.0-dev - -# bootstrap скрипт для проекта -COPY docker/php/bootstrap.sh /bootstrap.sh -# Вносим конфигурации для крона и воркеров -COPY docker/cron/* /etc/cron.d/ -COPY docker/supervisor/* /etc/supervisor/conf.d/ - -COPY id_rsa /root/.ssh/id_rsa - -# Включаем поддержку ssh -RUN chmod 400 ~/.ssh/id_rsa \ - && eval $(ssh-agent -s) \ - && ssh-add /root/.ssh/id_rsa \ - && touch /root/.ssh/known_hosts \ - && ssh-keyscan github.com gitlab.ely.by >> /root/.ssh/known_hosts - -# Копируем списки зависимостей composer в родительскую директорию, которая не будет синкаться -# с хостом через volume на dev окружении. В entrypoint эта папка будет скопирована обратно -COPY ./composer.json /var/www/composer.json -COPY ./composer.lock /var/www/composer.lock - -# Устанавливаем зависимости PHP -RUN cd .. \ - && composer install --no-interaction --no-suggest \ - && cd - - -# Устанавливаем зависимости для Node.js -# Делаем это отдельно, чтобы можно было воспользоваться кэшем, если от предыдущего билда -# ничего не менялось в зависимостях -RUN mkdir -p /var/www/frontend - -COPY ./frontend/package.json /var/www/frontend/ -COPY ./frontend/yarn.lock /var/www/frontend/ -COPY ./frontend/scripts /var/www/frontend/scripts -COPY ./frontend/webpack-utils /var/www/frontend/webpack-utils - -RUN cd /var/www/frontend \ - && yarn run build:install \ - && cd - - -# Наконец переносим все сорцы внутрь контейнера -COPY . /var/www/html - -# Билдим фронт -RUN cd frontend \ - && ln -s /var/www/frontend/node_modules $PWD/node_modules \ - && yarn run build:quiet \ - && rm node_modules \ - # Копируем билд наружу, чтобы его не затёрло volume в dev режиме - && cp -r ./dist /var/www/dist \ - && cd - diff --git a/composer.json b/composer.json index 9bf9e12..d6448ff 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,7 @@ }, { "type": "git", - "url": "git@gitlab.ely.by:elyby/email-renderer.git" + "url": "https://gitlab+deploy-token-1:FDGgmcnLdykcsyJJ_8Uv@gitlab.ely.by/elyby/email-renderer.git" } ], "autoload": { diff --git a/composer.lock b/composer.lock index 60f1fee..cba9321 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4334ca4dd8b377a9c40afd844527a850", + "content-hash": "7368afb90e5f3ed26a7b6d98551da170", "packages": [ { "name": "bacon/bacon-qr-code", @@ -615,7 +615,7 @@ "version": "dev-master", "source": { "type": "git", - "url": "git@gitlab.ely.by:elyby/email-renderer.git", + "url": "https://gitlab+deploy-token-1:FDGgmcnLdykcsyJJ_8Uv@gitlab.ely.by/elyby/email-renderer.git", "reference": "8aa2e71c5b3b8e4a726c3c090b2997030ba29f73" }, "require": { diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 7082ef4..7854586 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,9 +1,11 @@ version: '2' services: app: + image: registry.ely.by/elyby/accounts:dev build: - dockerfile: Dockerfile-dev context: . + args: + build_env: dev depends_on: - db - redis @@ -12,9 +14,11 @@ services: env_file: .env worker: + image: registry.ely.by/elyby/accounts:dev build: - dockerfile: Dockerfile-dev context: . + args: + build_env: dev command: ['php', 'yii', 'queue/listen', '-v'] depends_on: - db @@ -23,6 +27,20 @@ services: - ./:/var/www/html/ env_file: .env + cron: + image: registry.ely.by/elyby/accounts:dev + build: + context: . + args: + build_env: dev + command: ['crond', '-s', '/etc/cron.d', '-f', '-L', '/var/log/cron.log'] + depends_on: + - db + - redis + volumes: + - ./:/var/www/html/ + env_file: .env + web: image: registry.ely.by/elyby/accounts-nginx:latest volumes_from: @@ -58,17 +76,6 @@ services: - default - nginx-proxy - # Эта штука работает дико медленно, грузит процессор и т.д. и т.п. - # Раскоментировать только в случае лютой надобности - #node-dev-server: - # build: ./frontend - # ports: - # - "8080:8080" - # volumes: - # - ./frontend/:/usr/src/app/ - # environment: - # DOCKERIZED: "true" - networks: nginx-proxy: external: diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 539c878..687baab 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -17,6 +17,15 @@ services: - redis env_file: .env + worker: + image: registry.ely.by/elyby/accounts:latest + restart: always + command: ['crond', '-s', '/etc/cron.d', '-f', '-L', '/var/log/cron.log'] + depends_on: + - db + - redis + env_file: .env + web: image: registry.ely.by/elyby/accounts-nginx:1.0.3 restart: always diff --git a/docker/php/bootstrap.sh b/docker/php/bootstrap.sh deleted file mode 100755 index 65617dd..0000000 --- a/docker/php/bootstrap.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -mkdir -p api/runtime api/web/assets console/runtime -chown www-data:www-data api/runtime api/web/assets console/runtime - -if [ "$YII_ENV" = "test" ] -then - YII_EXEC="/var/www/html/tests/codeception/bin/yii" -else - YII_EXEC="/var/www/html/yii" -fi - -if ! cmp -s ./../vendor/autoload.php ./vendor/autoload.php -then - echo "Vendor have diffs" - echo "Removing not bundled vendor..." - rm -rf ./vendor - echo "Copying new one..." - cp -r ./../vendor ./vendor -fi - -# Переносим dist, если его нету или он изменился (или затёрся силами volume) -if ! cmp -s ./../dist/index.html ./frontend/dist/index.html -then - echo "Frontend dist have diffs" - echo "Removing not bundled dist..." - rm -rf ./frontend/dist - echo "Copying new one..." - cp -r ./../dist ./frontend/dist -fi - -# Генерируем правила RBAC -echo "Generating RBAC rules" -php $YII_EXEC rbac/generate - -if [ "$YII_ENV" != "test" ] -then - wait-for-it "${DB_HOST:-db}:3306" -s -t 0 -- "php $YII_EXEC migrate/up --interactive=0" -else - wait-for-it "${DB_HOST:-testdb}:3306" -s -t 0 -- "php $YII_EXEC migrate/up --interactive=0" -fi diff --git a/docker/php/docker-entrypoint.sh b/docker/php/docker-entrypoint.sh new file mode 100755 index 0000000..c34f9fa --- /dev/null +++ b/docker/php/docker-entrypoint.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +set -e + +XDEBUG_EXTENSION_FILE="/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini" +PHP_PROD_INI="/usr/local/etc/php/conf.d/php.prod.ini" +PHP_DEV_INI="/usr/local/etc/php/conf.d/php.dev.ini" + +if [ "$YII_DEBUG" = "true" ] || [ "$YII_DEBUG" = "1" ] ; then + echo "zend_extension=$(find /usr/local/lib/php/extensions/ -name xdebug.so)" > $XDEBUG_EXTENSION_FILE + mv ${PHP_PROD_INI}{,.disabled} 2> /dev/null || true + mv ${PHP_DEV_INI}{.disabled,} 2> /dev/null || true +else + rm -f $XDEBUG_EXTENSION_FILE + mv ${PHP_DEV_INI}{,.disabled} 2> /dev/null || true + mv ${PHP_PROD_INI}{.disabled,} 2> /dev/null || true +fi + +cd /var/www/html + +# Create all necessary folders +mkdir -p api/runtime api/web/assets console/runtime +chown -R www-data:www-data api/runtime api/web/assets console/runtime + +if [ "$YII_ENV" = "test" ] +then + YII_EXEC="/var/www/html/tests/codeception/bin/yii" +else + YII_EXEC="/var/www/html/yii" +fi + +# Fix permissions for cron tasks +chmod 644 /etc/cron.d/* + +if [ "$1" = "crond" ] ; then + # see: https://github.com/dubiousjim/dcron/issues/13 + # ignore using `exec` for `dcron` to get another pid instead of `1` + # exec "$@" + "$@" +fi + +if [ "$1" = "yii" ] ; then + shift + php $YII_EXEC "$@" + exit 0 +fi + +if [ "$1" = "sh" ] || [ "$1" = "bash" ] || [ "$1" = "composer" ] || [ "$1" = "php" ] ; then + exec "$@" + exit 0 +fi + +echo "Generating RBAC rules" +php $YII_EXEC rbac/generate + +if [ "$YII_ENV" != "test" ] +then + wait-for-it "${DB_HOST:-db}:3306" -s -t 0 -- "php $YII_EXEC migrate/up --interactive=0" +else + wait-for-it "${DB_HOST:-testdb}:3306" -s -t 0 -- "php $YII_EXEC migrate/up --interactive=0" +fi + +exec "$@" diff --git a/docker/php/php.dev.ini b/docker/php/php.dev.ini new file mode 100644 index 0000000..7114082 --- /dev/null +++ b/docker/php/php.dev.ini @@ -0,0 +1,5 @@ +error_reporting = E_ALL; +display_errors = On; + +# Disable Opcache caching +opcache.validate_timestamps = 1; diff --git a/docker/php/php.prod.ini b/docker/php/php.prod.ini new file mode 100644 index 0000000..f0cc3e5 --- /dev/null +++ b/docker/php/php.prod.ini @@ -0,0 +1,14 @@ +error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT +display_errors = Off +display_startup_errors = Off +log_errors = On +html_errors = Off +expose_php = Off + +# Opcache tuning +opcache.revalidate_freq = 0 +opcache.validate_timestamps = 0 +opcache.max_accelerated_files = 7963 +opcache.memory_consumption = 192 +opcache.interned_strings_buffer = 16 +opcache.fast_shutdown = 1 diff --git a/docker/wait-for-it.sh b/docker/php/wait-for-it.sh similarity index 100% rename from docker/wait-for-it.sh rename to docker/php/wait-for-it.sh From 40eea7864e8e15d99ecb846b50ef737f6498233f Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Tue, 10 Jul 2018 20:08:43 +0300 Subject: [PATCH 13/19] Fix gitlab-ci --- .gitlab-ci.yml | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3a5aacb..5328d39 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,26 +9,45 @@ variables: check backend codestyle: image: edbizarro/gitlab-ci-pipeline-php:7.2-alpine stage: test + cache: + paths: + - vendor script: + - composer install - vendor/bin/php-cs-fixer fix -v --dry-run test backend: image: edbizarro/gitlab-ci-pipeline-php:7.2-alpine services: - - mariadb:10.2.11 - - redis:3.0-alpine + - name: redis:4.0.10-alpine + alias: redis + - name: mariadb:10.2.11 + alias: db variables: - # app config - YII_ENV: "test" - YII_DEBUG: "true" - # mariadb config + # App config + DB_HOST: "db" + DB_DATABASE: "ely_accounts_test" + DB_USER: "ely_accounts_tester" + DB_PASSWORD: "ely_accounts_tester_password" + REDIS_HOST: "redis" + REDIS_PORT: "6379" + # MariaDB config MYSQL_RANDOM_ROOT_PASSWORD: "true" MYSQL_DATABASE: "ely_accounts_test" MYSQL_USER: "ely_accounts_tester" MYSQL_PASSWORD: "ely_accounts_tester_password" stage: test + cache: + paths: + - vendor + before_script: + # While we not counting coverage, xdebug only slow down tests + - sudo rm /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini script: - - docker-entrypoint.sh vendor/bin/codecept run -c tests + - composer install + - php tests/codeception/bin/yii rbac/generate + - ./docker/php/wait-for-it.sh "${DB_HOST}:3306" -s -t 0 -- "php tests/codeception/bin/yii migrate/up --interactive=0" + - vendor/bin/codecept run -c tests test frontend: image: node:9.2.1-alpine @@ -71,7 +90,7 @@ build:production: - sed -i"" -e "s/{{PLACE_VERSION_HERE}}/$VERSION/g" common/config/config.php script: - export IMAGE_NAME="$CONTAINER_IMAGE:latest" - - docker build --pull -t $IMAGE_NAME . + - docker build --pull --build-arg build_env=prod -t $IMAGE_NAME . only: - develop - tags From 9ed449d144eed624bfef124c1dba25eab46407ee Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Wed, 11 Jul 2018 01:00:47 +0300 Subject: [PATCH 14/19] Use same cache for all backend builds --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5328d39..ce5a2be 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,6 +10,7 @@ check backend codestyle: image: edbizarro/gitlab-ci-pipeline-php:7.2-alpine stage: test cache: + key: backend-vendor paths: - vendor script: @@ -38,6 +39,7 @@ test backend: MYSQL_PASSWORD: "ely_accounts_tester_password" stage: test cache: + key: backend-vendor paths: - vendor before_script: From 262142a835f057de8a3fdf2c2bb8c451a8926c9a Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Wed, 11 Jul 2018 01:38:48 +0300 Subject: [PATCH 15/19] Disable csrf cookies for api --- api/config/config.php | 1 + 1 file changed, 1 insertion(+) diff --git a/api/config/config.php b/api/config/config.php index 5637f01..ffe0ee2 100644 --- a/api/config/config.php +++ b/api/config/config.php @@ -58,6 +58,7 @@ return [ ], 'request' => [ 'baseUrl' => '/api', + 'enableCsrfCookie' => false, 'parsers' => [ '*' => api\request\RequestParser::class, ], From 069e4876dde245d432b6e4fb00fdc700c21a37a9 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Wed, 11 Jul 2018 02:23:46 +0300 Subject: [PATCH 16/19] Restore xdebug configuration --- docker/php/xdebug.ini | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 docker/php/xdebug.ini diff --git a/docker/php/xdebug.ini b/docker/php/xdebug.ini new file mode 100644 index 0000000..431323d --- /dev/null +++ b/docker/php/xdebug.ini @@ -0,0 +1,11 @@ +xdebug.default_enable=1 +xdebug.remote_enable=1 +xdebug.remote_handler=dbgp +xdebug.remote_mode=req +xdebug.remote_autostart=1 +xdebug.remote_port=9000 +xdebug.remote_connect_back=0 +xdebug.cli_color=1 +xdebug.var_display_max_depth=10 +xdebug.profiler_enable_trigger=1 +xdebug.profiler_output_dir=/tmp/xdebug-profiler From 92ee2830393314c8fa97c4593bef323ed81ffea7 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Wed, 11 Jul 2018 02:24:05 +0300 Subject: [PATCH 17/19] Upgrade tests environment --- tests/.gitignore | 1 + tests/docker-compose.yml | 5 +++-- tests/php.sh | 6 ------ tests/run-tests.sh | 6 ------ 4 files changed, 4 insertions(+), 14 deletions(-) create mode 100644 tests/.gitignore delete mode 100755 tests/php.sh delete mode 100755 tests/run-tests.sh diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..c40b483 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +.bash_history diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml index 42c486d..5a8fb5b 100644 --- a/tests/docker-compose.yml +++ b/tests/docker-compose.yml @@ -2,10 +2,11 @@ version: '2' services: testphp: container_name: accountelyby_testphp - image: account_testphp + image: registry.ely.by/elyby/accounts:dev build: context: ../ - dockerfile: Dockerfile-dev + args: + build_env: dev depends_on: - testdb - testredis diff --git a/tests/php.sh b/tests/php.sh deleted file mode 100755 index c6a53a2..0000000 --- a/tests/php.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -cd "$(dirname "$0")" - -./../vendor/bin/codecept build -./../vendor/bin/codecept run $* diff --git a/tests/run-tests.sh b/tests/run-tests.sh deleted file mode 100755 index 7ff68f2..0000000 --- a/tests/run-tests.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -cd "$(dirname "$0")" - -docker-compose run --rm testphp ./tests/php.sh $* -docker-compose stop # docker не останавливает зависимые контейнеры после завершения работы главного процесса From 903f453afa3786a4a96ac461a394df42bdb89483 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Wed, 11 Jul 2018 02:24:15 +0300 Subject: [PATCH 18/19] Fix RegisterCest --- .../api/functional/RegisterCest.php | 523 ++++++++++-------- 1 file changed, 282 insertions(+), 241 deletions(-) diff --git a/tests/codeception/api/functional/RegisterCest.php b/tests/codeception/api/functional/RegisterCest.php index 4730d8f..c4ee698 100644 --- a/tests/codeception/api/functional/RegisterCest.php +++ b/tests/codeception/api/functional/RegisterCest.php @@ -1,258 +1,299 @@ 'erickskrauch@ely.by', - 'username' => 'ErickSkrauch', - ]); + /** + * @var SignupRoute + */ + private $route; + + public function _before(FunctionalTester $I) { + $this->route = new SignupRoute($I); } - public function testIncorrectRegistration(FunctionalTester $I) { - $route = new SignupRoute($I); - - $I->wantTo('get error.rulesAgreement_required if we don\'t accept rules'); - $route->register([ - 'username' => 'ErickSkrauch', - 'email' => 'erickskrauch@ely.by', - 'password' => 'some_password', - 'rePassword' => 'some_password', - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'rulesAgreement' => 'error.rulesAgreement_required', - ], - ]); - - $I->wantTo('don\'t see error.rulesAgreement_requireds if we accept rules'); - $route->register([ - 'rulesAgreement' => true, - ]); - $I->cantSeeResponseContainsJson([ - 'errors' => [ - 'rulesAgreement' => 'error.rulesAgreement_required', - ], - ]); - - $I->wantTo('see error.username_required if username is not set'); - $route->register([ - 'username' => '', - 'email' => '', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'username' => 'error.username_required', - ], - ]); - - $I->wantTo('don\'t see error.username_required if username is not set'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => '', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->cantSeeResponseContainsJson([ - 'errors' => [ - 'username' => 'error.username_required', - ], - ]); - - $I->wantTo('see error.email_required if email is not set'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => '', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'email' => 'error.email_required', - ], - ]); - - $I->wantTo('see error.email_invalid if email is set, but invalid'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'invalid@email', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'email' => 'error.email_invalid', - ], - ]); - - $I->wantTo('see error.email_invalid if email is set, valid, but domain doesn\'t exist or don\'t have mx record'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'invalid@govnomail.com', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'email' => 'error.email_invalid', - ], - ]); - - $I->wantTo('see error.email_not_available if email is set, fully valid, but not available for registration'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'admin@ely.by', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'email' => 'error.email_not_available', - ], - ]); - - $I->wantTo('don\'t see errors on email if all valid'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'erickskrauch@ely.by', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->cantSeeResponseJsonMatchesJsonPath('$.errors.email'); - - $I->wantTo('see error.password_required if password is not set'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'erickskrauch@ely.by', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'password' => 'error.password_required', - ], - ]); - - $I->wantTo('see error.password_too_short before it will be compared with rePassword'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'correct-email@ely.by', - 'password' => 'short', - 'rePassword' => 'password', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'password' => 'error.password_too_short', - ], - ]); - $I->cantSeeResponseJsonMatchesJsonPath('$.errors.rePassword'); - - $I->wantTo('see error.rePassword_required if password valid and rePassword not set'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'correct-email@ely.by', - 'password' => 'valid-password', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'rePassword' => 'error.rePassword_required', - ], - ]); - - $I->wantTo('see error.rePassword_does_not_match if password valid and rePassword donen\'t match it'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'correct-email@ely.by', - 'password' => 'valid-password', - 'rePassword' => 'password', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'rePassword' => 'error.rePassword_does_not_match', - ], - ]); - $I->cantSeeResponseJsonMatchesJsonPath('$.errors.password'); - } - - public function testUserCorrectRegistration(FunctionalTester $I) { - $route = new SignupRoute($I); - - $I->wantTo('ensure that signup works'); - $route->register([ - 'username' => 'some_username', - 'email' => 'some_email@example.com', - 'password' => 'some_password', - 'rePassword' => 'some_password', - 'rulesAgreement' => true, - 'lang' => 'ru', - ]); - $this->assertSuccessRegistration($I); - } - - public function testUserCorrectRegistrationWithReassignUsername(FunctionalTester $I) { - $route = new SignupRoute($I); - - $I->wantTo('ensure that signup allow reassign not finished registration username'); - $route->register([ - 'username' => 'howe.garnett', - 'email' => 'custom-email@gmail.com', - 'password' => 'some_password', - 'rePassword' => 'some_password', - 'rulesAgreement' => true, - 'lang' => 'ru', - ]); - $this->assertSuccessRegistration($I); - } - - public function testUserCorrectRegistrationWithReassignEmail(FunctionalTester $I) { - $route = new SignupRoute($I); - - $I->wantTo('ensure that signup allow reassign not finished registration email'); - $route->register([ - 'username' => 'CustomUsername', - 'email' => 'achristiansen@gmail.com', - 'password' => 'some_password', - 'rePassword' => 'some_password', - 'rulesAgreement' => true, - 'lang' => 'ru', - ]); - $this->assertSuccessRegistration($I); - } - - private function assertSuccessRegistration(FunctionalTester $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', + ], + ], + ]; + } + } From 9620ff75eb67fb69fcaa31c3c7d9054f19d3e467 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Fri, 26 Oct 2018 16:45:05 +0300 Subject: [PATCH 19/19] Update glibc installation in gitlab-ci --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ce5a2be..6fda076 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -60,10 +60,10 @@ test frontend: before_script: # Enable SSL support for wget - apk add --update openssl - # https://github.com/facebook/flow/issues/3649#issuecomment-308070179 - - wget -O /etc/apk/keys/sgerrand.rsa.pub https://raw.githubusercontent.com/sgerrand/alpine-pkg-glibc/master/sgerrand.rsa.pub - - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.25-r0/glibc-2.25-r0.apk - - apk add glibc-2.25-r0.apk + # https://github.com/facebook/flow/issues/3649#issuecomment-414691014 + - wget -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub + - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.28-r0/glibc-2.28-r0.apk + - apk add glibc-2.28-r0.apk script: - cd frontend - yarn run build:install