mirror of
				https://github.com/elyby/accounts.git
				synced 2025-05-31 14:11:46 +05:30 
			
		
		
		
	Merge branch 'ban_user_api' into develop
This commit is contained in:
		@@ -6,8 +6,8 @@ use yii\web\User as YiiUserComponent;
 | 
			
		||||
/**
 | 
			
		||||
 * @property Identity|null $identity
 | 
			
		||||
 *
 | 
			
		||||
 * @method Identity|null getIdentity()
 | 
			
		||||
 * @method Identity|null loginByAccessToken(string $token, $type = null)
 | 
			
		||||
 * @method Identity|null getIdentity($autoRenew = true)
 | 
			
		||||
 * @method Identity|null loginByAccessToken($token, $type = null)
 | 
			
		||||
 */
 | 
			
		||||
class Component extends YiiUserComponent {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,8 @@ class Identity implements IdentityInterface {
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    public static function findIdentityByAccessToken($token, $type = null) {
 | 
			
		||||
    public static function findIdentityByAccessToken($token, $type = null): self {
 | 
			
		||||
        /** @var AccessTokenEntity|null $model */
 | 
			
		||||
        $model = Yii::$app->oauth->getAuthServer()->getAccessTokenStorage()->get($token);
 | 
			
		||||
        if ($model === null) {
 | 
			
		||||
            throw new UnauthorizedHttpException('Incorrect token');
 | 
			
		||||
@@ -41,19 +42,19 @@ class Identity implements IdentityInterface {
 | 
			
		||||
        $this->_accessToken = $accessToken;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getAccount() : Account {
 | 
			
		||||
    public function getAccount(): Account {
 | 
			
		||||
        return $this->getSession()->account;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getClient() : OauthClient {
 | 
			
		||||
    public function getClient(): OauthClient {
 | 
			
		||||
        return $this->getSession()->client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getSession() : OauthSession {
 | 
			
		||||
    public function getSession(): OauthSession {
 | 
			
		||||
        return OauthSession::findOne($this->_accessToken->getSessionId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getAccessToken() : AccessTokenEntity {
 | 
			
		||||
    public function getAccessToken(): AccessTokenEntity {
 | 
			
		||||
        return $this->_accessToken;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -62,7 +63,7 @@ class Identity implements IdentityInterface {
 | 
			
		||||
     * У нас права привязываются к токенам, так что возвращаем именно его id.
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    public function getId() {
 | 
			
		||||
    public function getId(): string {
 | 
			
		||||
        return $this->_accessToken->getId();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,8 @@ namespace api\components\OAuth2\Entities;
 | 
			
		||||
 | 
			
		||||
class ClientEntity extends \League\OAuth2\Server\Entity\ClientEntity {
 | 
			
		||||
 | 
			
		||||
    private $isTrusted;
 | 
			
		||||
 | 
			
		||||
    public function setId(string $id) {
 | 
			
		||||
        $this->id = $id;
 | 
			
		||||
    }
 | 
			
		||||
@@ -19,4 +21,12 @@ class ClientEntity extends \League\OAuth2\Server\Entity\ClientEntity {
 | 
			
		||||
        $this->redirectUri = $redirectUri;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setIsTrusted(bool $isTrusted) {
 | 
			
		||||
        $this->isTrusted = $isTrusted;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function isTrusted(): bool {
 | 
			
		||||
        return $this->isTrusted;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								api/components/OAuth2/Grants/ClientCredentialsGrant.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								api/components/OAuth2/Grants/ClientCredentialsGrant.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace api\components\OAuth2\Grants;
 | 
			
		||||
 | 
			
		||||
use api\components\OAuth2\Entities;
 | 
			
		||||
 | 
			
		||||
class ClientCredentialsGrant extends \League\OAuth2\Server\Grant\ClientCredentialsGrant {
 | 
			
		||||
 | 
			
		||||
    protected function createAccessTokenEntity() {
 | 
			
		||||
        return new Entities\AccessTokenEntity($this->server);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function createRefreshTokenEntity() {
 | 
			
		||||
        return new Entities\RefreshTokenEntity($this->server);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function createSessionEntity() {
 | 
			
		||||
        return new Entities\SessionEntity($this->server);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -74,6 +74,7 @@ class ClientStorage extends AbstractStorage implements ClientInterface {
 | 
			
		||||
        $entity->setId($model->id);
 | 
			
		||||
        $entity->setName($model->name);
 | 
			
		||||
        $entity->setSecret($model->secret);
 | 
			
		||||
        $entity->setIsTrusted($model->is_trusted);
 | 
			
		||||
        $entity->setRedirectUri($model->redirect_uri);
 | 
			
		||||
 | 
			
		||||
        return $entity;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,12 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace api\components\OAuth2\Storage;
 | 
			
		||||
 | 
			
		||||
use api\components\OAuth2\Entities\ClientEntity;
 | 
			
		||||
use api\components\OAuth2\Entities\ScopeEntity;
 | 
			
		||||
use common\models\OauthScope;
 | 
			
		||||
use League\OAuth2\Server\Storage\AbstractStorage;
 | 
			
		||||
use League\OAuth2\Server\Storage\ScopeInterface;
 | 
			
		||||
use yii\base\ErrorException;
 | 
			
		||||
 | 
			
		||||
class ScopeStorage extends AbstractStorage implements ScopeInterface {
 | 
			
		||||
 | 
			
		||||
@@ -12,7 +14,28 @@ class ScopeStorage extends AbstractStorage implements ScopeInterface {
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    public function get($scope, $grantType = null, $clientId = null) {
 | 
			
		||||
        if (!in_array($scope, OauthScope::getScopes(), true)) {
 | 
			
		||||
        $query = OauthScope::find();
 | 
			
		||||
        if ($grantType === 'authorization_code') {
 | 
			
		||||
            $query->onlyPublic()->usersScopes();
 | 
			
		||||
        } elseif ($grantType === 'client_credentials') {
 | 
			
		||||
            $query->machineScopes();
 | 
			
		||||
            $isTrusted = false;
 | 
			
		||||
            if ($clientId !== null) {
 | 
			
		||||
                $client = $this->server->getClientStorage()->get($clientId);
 | 
			
		||||
                if (!$client instanceof ClientEntity) {
 | 
			
		||||
                    throw new ErrorException('client storage must return instance of ' . ClientEntity::class);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $isTrusted = $client->isTrusted();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!$isTrusted) {
 | 
			
		||||
                $query->onlyPublic();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $scopes = $query->all();
 | 
			
		||||
        if (!in_array($scope, $scopes, true)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ $params = array_merge(
 | 
			
		||||
return [
 | 
			
		||||
    'id' => 'accounts-site-api',
 | 
			
		||||
    'basePath' => dirname(__DIR__),
 | 
			
		||||
    'bootstrap' => ['log', 'authserver'],
 | 
			
		||||
    'bootstrap' => ['log', 'authserver', 'internal'],
 | 
			
		||||
    'controllerNamespace' => 'api\controllers',
 | 
			
		||||
    'params' => $params,
 | 
			
		||||
    'components' => [
 | 
			
		||||
@@ -73,14 +73,6 @@ return [
 | 
			
		||||
        'response' => [
 | 
			
		||||
            'format' => yii\web\Response::FORMAT_JSON,
 | 
			
		||||
        ],
 | 
			
		||||
        'oauth' => [
 | 
			
		||||
            'class' => api\components\OAuth2\Component::class,
 | 
			
		||||
            'grantTypes' => ['authorization_code'],
 | 
			
		||||
            'grantMap' => [
 | 
			
		||||
                'authorization_code' => api\components\OAuth2\Grants\AuthCodeGrant::class,
 | 
			
		||||
                'refresh_token' => api\components\OAuth2\Grants\RefreshTokenGrant::class,
 | 
			
		||||
            ],
 | 
			
		||||
        ],
 | 
			
		||||
        'errorHandler' => [
 | 
			
		||||
            'class' => api\components\ErrorHandler::class,
 | 
			
		||||
        ],
 | 
			
		||||
@@ -96,5 +88,8 @@ return [
 | 
			
		||||
        'mojang' => [
 | 
			
		||||
            'class' => api\modules\mojang\Module::class,
 | 
			
		||||
        ],
 | 
			
		||||
        'internal' => [
 | 
			
		||||
            'class' => api\modules\internal\Module::class,
 | 
			
		||||
        ],
 | 
			
		||||
    ],
 | 
			
		||||
];
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,9 @@ use api\components\OAuth2\Exception\AccessDeniedException;
 | 
			
		||||
use common\models\Account;
 | 
			
		||||
use common\models\OauthClient;
 | 
			
		||||
use common\models\OauthScope;
 | 
			
		||||
use League\OAuth2\Server\AuthorizationServer;
 | 
			
		||||
use League\OAuth2\Server\Exception\OAuthException;
 | 
			
		||||
use League\OAuth2\Server\Grant\AuthCodeGrant;
 | 
			
		||||
use Yii;
 | 
			
		||||
use yii\filters\AccessControl;
 | 
			
		||||
use yii\helpers\ArrayHelper;
 | 
			
		||||
@@ -274,17 +276,12 @@ class OauthController extends Controller {
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return \League\OAuth2\Server\AuthorizationServer
 | 
			
		||||
     */
 | 
			
		||||
    private function getServer() {
 | 
			
		||||
    private function getServer(): AuthorizationServer {
 | 
			
		||||
        return Yii::$app->oauth->authServer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return \League\OAuth2\Server\Grant\AuthCodeGrant
 | 
			
		||||
     */
 | 
			
		||||
    private function getGrantType() {
 | 
			
		||||
    private function getGrantType(): AuthCodeGrant {
 | 
			
		||||
        /** @noinspection PhpIncompatibleReturnTypeInspection */
 | 
			
		||||
        return $this->getServer()->getGrantType('authorization_code');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								api/modules/internal/Module.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								api/modules/internal/Module.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace api\modules\internal;
 | 
			
		||||
 | 
			
		||||
use yii\base\BootstrapInterface;
 | 
			
		||||
 | 
			
		||||
class Module extends \yii\base\Module implements BootstrapInterface {
 | 
			
		||||
 | 
			
		||||
    public $id = 'internal';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param \yii\base\Application $app the application currently running
 | 
			
		||||
     */
 | 
			
		||||
    public function bootstrap($app) {
 | 
			
		||||
        $app->getUrlManager()->addRules([
 | 
			
		||||
            '/internal/<controller>/<accountId>/<action>' => "{$this->id}/<controller>/<action>",
 | 
			
		||||
        ], false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										88
									
								
								api/modules/internal/controllers/AccountsController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								api/modules/internal/controllers/AccountsController.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace api\modules\internal\controllers;
 | 
			
		||||
 | 
			
		||||
use api\components\ApiUser\AccessControl;
 | 
			
		||||
use api\controllers\Controller;
 | 
			
		||||
use api\modules\internal\models\BanForm;
 | 
			
		||||
use api\modules\internal\models\PardonForm;
 | 
			
		||||
use common\models\Account;
 | 
			
		||||
use common\models\OauthScope as S;
 | 
			
		||||
use Yii;
 | 
			
		||||
use yii\helpers\ArrayHelper;
 | 
			
		||||
use yii\web\NotFoundHttpException;
 | 
			
		||||
 | 
			
		||||
class AccountsController extends Controller {
 | 
			
		||||
 | 
			
		||||
    public function behaviors() {
 | 
			
		||||
        return ArrayHelper::merge(parent::behaviors(), [
 | 
			
		||||
            'authenticator' => [
 | 
			
		||||
                'user' => Yii::$app->apiUser,
 | 
			
		||||
            ],
 | 
			
		||||
            'access' => [
 | 
			
		||||
                'class' => AccessControl::class,
 | 
			
		||||
                'rules' => [
 | 
			
		||||
                    [
 | 
			
		||||
                        'actions' => ['ban'],
 | 
			
		||||
                        'allow' => true,
 | 
			
		||||
                        'roles' => [S::ACCOUNT_BLOCK],
 | 
			
		||||
                    ],
 | 
			
		||||
                ],
 | 
			
		||||
            ],
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function verbs() {
 | 
			
		||||
        return [
 | 
			
		||||
            'ban' => ['POST', 'DELETE'],
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function actionBan(int $accountId) {
 | 
			
		||||
        $account = $this->findAccount($accountId);
 | 
			
		||||
        if (Yii::$app->request->isPost) {
 | 
			
		||||
            return $this->banAccount($account);
 | 
			
		||||
        } else {
 | 
			
		||||
            return $this->pardonAccount($account);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function banAccount(Account $account) {
 | 
			
		||||
        $model = new BanForm($account);
 | 
			
		||||
        $model->load(Yii::$app->request->post());
 | 
			
		||||
        if (!$model->ban()) {
 | 
			
		||||
            return [
 | 
			
		||||
                'success' => false,
 | 
			
		||||
                'errors' => $model->getFirstErrors(),
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            'success' => true,
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function pardonAccount(Account $account) {
 | 
			
		||||
        $model = new PardonForm($account);
 | 
			
		||||
        $model->load(Yii::$app->request->post());
 | 
			
		||||
        if (!$model->pardon()) {
 | 
			
		||||
            return [
 | 
			
		||||
                'success' => false,
 | 
			
		||||
                'errors' => $model->getFirstErrors(),
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            'success' => true,
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function findAccount(int $accountId): Account {
 | 
			
		||||
        $account = Account::findOne($accountId);
 | 
			
		||||
        if ($account === null) {
 | 
			
		||||
            throw new NotFoundHttpException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $account;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								api/modules/internal/helpers/Error.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								api/modules/internal/helpers/Error.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace api\modules\internal\helpers;
 | 
			
		||||
 | 
			
		||||
final class Error {
 | 
			
		||||
 | 
			
		||||
    public const ACCOUNT_ALREADY_BANNED = 'error.account_already_banned';
 | 
			
		||||
    public const ACCOUNT_NOT_BANNED = 'error.account_not_banned';
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										95
									
								
								api/modules/internal/models/BanForm.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								api/modules/internal/models/BanForm.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace api\modules\internal\models;
 | 
			
		||||
 | 
			
		||||
use api\models\base\ApiForm;
 | 
			
		||||
use api\modules\internal\helpers\Error as E;
 | 
			
		||||
use common\helpers\Amqp;
 | 
			
		||||
use common\models\Account;
 | 
			
		||||
use common\models\amqp\AccountBanned;
 | 
			
		||||
use PhpAmqpLib\Message\AMQPMessage;
 | 
			
		||||
use Yii;
 | 
			
		||||
use yii\base\ErrorException;
 | 
			
		||||
 | 
			
		||||
class BanForm extends ApiForm {
 | 
			
		||||
 | 
			
		||||
    public const DURATION_FOREVER = -1;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Нереализованный функционал блокировки аккаунта на определённый период времени.
 | 
			
		||||
     * Сейчас установка этого параметра ничего не даст, аккаунт будет заблокирован навечно,
 | 
			
		||||
     * но, по задумке, здесь можно передать количество секунд, на которое будет
 | 
			
		||||
     * заблокирован аккаунт пользователя.
 | 
			
		||||
     *
 | 
			
		||||
     * @var int
 | 
			
		||||
     */
 | 
			
		||||
    public $duration = self::DURATION_FOREVER;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Нереализованный функционал указания причины блокировки аккаунта.
 | 
			
		||||
     *
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    public $message = '';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Account
 | 
			
		||||
     */
 | 
			
		||||
    private $account;
 | 
			
		||||
 | 
			
		||||
    public function rules(): array {
 | 
			
		||||
        return [
 | 
			
		||||
            [['duration'], 'integer', 'min' => self::DURATION_FOREVER],
 | 
			
		||||
            [['message'], 'string'],
 | 
			
		||||
            [['account'], 'validateAccountActivity'],
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getAccount(): Account {
 | 
			
		||||
        return $this->account;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function validateAccountActivity() {
 | 
			
		||||
        if ($this->account->status === Account::STATUS_BANNED) {
 | 
			
		||||
            $this->addError('account', E::ACCOUNT_ALREADY_BANNED);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function ban(): bool {
 | 
			
		||||
        if (!$this->validate()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $transaction = Yii::$app->db->beginTransaction();
 | 
			
		||||
 | 
			
		||||
        $account = $this->account;
 | 
			
		||||
        $account->status = Account::STATUS_BANNED;
 | 
			
		||||
        if (!$account->save()) {
 | 
			
		||||
            throw new ErrorException('Cannot ban account');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->createTask();
 | 
			
		||||
 | 
			
		||||
        $transaction->commit();
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function createTask(): void {
 | 
			
		||||
        $model = new AccountBanned();
 | 
			
		||||
        $model->accountId = $this->account->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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __construct(Account $account, array $config = []) {
 | 
			
		||||
        $this->account = $account;
 | 
			
		||||
        parent::__construct($config);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								api/modules/internal/models/PardonForm.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								api/modules/internal/models/PardonForm.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace api\modules\internal\models;
 | 
			
		||||
 | 
			
		||||
use api\models\base\ApiForm;
 | 
			
		||||
use api\modules\internal\helpers\Error as E;
 | 
			
		||||
use common\helpers\Amqp;
 | 
			
		||||
use common\models\Account;
 | 
			
		||||
use common\models\amqp\AccountPardoned;
 | 
			
		||||
use PhpAmqpLib\Message\AMQPMessage;
 | 
			
		||||
use Yii;
 | 
			
		||||
use yii\base\ErrorException;
 | 
			
		||||
 | 
			
		||||
class PardonForm extends ApiForm {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Account
 | 
			
		||||
     */
 | 
			
		||||
    private $account;
 | 
			
		||||
 | 
			
		||||
    public function rules(): array {
 | 
			
		||||
        return [
 | 
			
		||||
            [['account'], 'validateAccountBanned'],
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getAccount(): Account {
 | 
			
		||||
        return $this->account;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function validateAccountBanned(): void {
 | 
			
		||||
        if ($this->account->status !== Account::STATUS_BANNED) {
 | 
			
		||||
            $this->addError('account', E::ACCOUNT_NOT_BANNED);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function pardon(): bool {
 | 
			
		||||
        if (!$this->validate()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $transaction = Yii::$app->db->beginTransaction();
 | 
			
		||||
 | 
			
		||||
        $account = $this->account;
 | 
			
		||||
        $account->status = Account::STATUS_ACTIVE;
 | 
			
		||||
        if (!$account->save()) {
 | 
			
		||||
            throw new ErrorException('Cannot pardon account');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->createTask();
 | 
			
		||||
 | 
			
		||||
        $transaction->commit();
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function createTask(): void {
 | 
			
		||||
        $model = new AccountPardoned();
 | 
			
		||||
        $model->accountId = $this->account->id;
 | 
			
		||||
 | 
			
		||||
        $message = Amqp::getInstance()->prepareMessage($model, [
 | 
			
		||||
            'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        Amqp::sendToEventsExchange('accounts.account-pardoned', $message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __construct(Account $account, array $config = []) {
 | 
			
		||||
        $this->account = $account;
 | 
			
		||||
        parent::__construct($config);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -16,12 +16,13 @@ class Yii extends \yii\BaseYii {
 | 
			
		||||
 * Class BaseApplication
 | 
			
		||||
 * Used for properties that are identical for both WebApplication and ConsoleApplication
 | 
			
		||||
 *
 | 
			
		||||
 * @property \yii\swiftmailer\Mailer $mailer
 | 
			
		||||
 * @property \common\components\Redis\Connection $redis
 | 
			
		||||
 * @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 \GuzzleHttp\Client                    $guzzle
 | 
			
		||||
 * @property \common\components\EmailRenderer      $emailRenderer
 | 
			
		||||
 * @property \mito\sentry\Component                $sentry
 | 
			
		||||
 * @property \api\components\OAuth2\Component      $oauth
 | 
			
		||||
 */
 | 
			
		||||
abstract class BaseApplication extends yii\base\Application {
 | 
			
		||||
}
 | 
			
		||||
@@ -33,7 +34,6 @@ abstract class BaseApplication extends yii\base\Application {
 | 
			
		||||
 * @property \api\components\User\Component      $user User component.
 | 
			
		||||
 * @property \api\components\ApiUser\Component   $apiUser Api User component.
 | 
			
		||||
 * @property \api\components\ReCaptcha\Component $reCaptcha
 | 
			
		||||
 * @property \api\components\OAuth2\Component    $oauth
 | 
			
		||||
 *
 | 
			
		||||
 * @method \api\components\User\Component getUser()
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								common/components/Annotations/Reader.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								common/components/Annotations/Reader.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace common\components\Annotations;
 | 
			
		||||
 | 
			
		||||
class Reader extends \Minime\Annotations\Reader {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Поначаду я думал кэшировать эту штуку, но потом забил, т.к. всё всё равно завернул
 | 
			
		||||
     * в Yii::$app->cache и как-то надобность в отдельном кэше отпала, так что пока забьём
 | 
			
		||||
     * и оставим как заготовку на будущее
 | 
			
		||||
     *
 | 
			
		||||
     * @return \Minime\Annotations\Interfaces\ReaderInterface
 | 
			
		||||
     */
 | 
			
		||||
    public static function createFromDefaults() {
 | 
			
		||||
        return parent::createFromDefaults();
 | 
			
		||||
        //return new self(new \Minime\Annotations\Parser(), new RedisCache());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										65
									
								
								common/components/Annotations/RedisCache.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								common/components/Annotations/RedisCache.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace common\components\Annotations;
 | 
			
		||||
 | 
			
		||||
use common\components\Redis\Key;
 | 
			
		||||
use common\components\Redis\Set;
 | 
			
		||||
use Minime\Annotations\Interfaces\CacheInterface;
 | 
			
		||||
use yii\helpers\Json;
 | 
			
		||||
 | 
			
		||||
class RedisCache implements CacheInterface {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generates uuid for a given docblock string
 | 
			
		||||
     * @param  string $docblock docblock string
 | 
			
		||||
     * @return string uuid that maps to the given docblock
 | 
			
		||||
     */
 | 
			
		||||
    public function getKey($docblock) {
 | 
			
		||||
        return md5($docblock);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds an annotation AST to cache
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $key cache entry uuid
 | 
			
		||||
     * @param array  $annotations annotation AST
 | 
			
		||||
     */
 | 
			
		||||
    public function set($key, array $annotations) {
 | 
			
		||||
        $this->getRedisKey($key)->setValue(Json::encode($annotations))->expire(3600);
 | 
			
		||||
        $this->getRedisKeysSet()->add($key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves cached annotations from docblock uuid
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string $key cache entry uuid
 | 
			
		||||
     * @return array  cached annotation AST
 | 
			
		||||
     */
 | 
			
		||||
    public function get($key) {
 | 
			
		||||
        $result = $this->getRedisKey($key)->getValue();
 | 
			
		||||
        if ($result === null) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Json::decode($result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resets cache
 | 
			
		||||
     */
 | 
			
		||||
    public function clear() {
 | 
			
		||||
        /** @var array $keys */
 | 
			
		||||
        $keys = $this->getRedisKeysSet()->getValue();
 | 
			
		||||
        foreach ($keys as $key) {
 | 
			
		||||
            $this->getRedisKey($key)->delete();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function getRedisKey(string $key): Key {
 | 
			
		||||
        return new Key('annotations', 'cache', $key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function getRedisKeysSet(): Set {
 | 
			
		||||
        return new Set('annotations', 'cache', 'keys');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -69,6 +69,15 @@ return [
 | 
			
		||||
            'class' => common\components\EmailRenderer::class,
 | 
			
		||||
            'basePath' => '/images/emails',
 | 
			
		||||
        ],
 | 
			
		||||
        'oauth' => [
 | 
			
		||||
            'class' => api\components\OAuth2\Component::class,
 | 
			
		||||
            'grantTypes' => ['authorization_code', 'client_credentials'],
 | 
			
		||||
            'grantMap' => [
 | 
			
		||||
                'authorization_code' => api\components\OAuth2\Grants\AuthCodeGrant::class,
 | 
			
		||||
                'refresh_token' => api\components\OAuth2\Grants\RefreshTokenGrant::class,
 | 
			
		||||
                'client_credentials' => api\components\OAuth2\Grants\ClientCredentialsGrant::class,
 | 
			
		||||
            ],
 | 
			
		||||
        ],
 | 
			
		||||
    ],
 | 
			
		||||
    'aliases' => [
 | 
			
		||||
        '@bower' => '@vendor/bower-asset',
 | 
			
		||||
 
 | 
			
		||||
@@ -29,10 +29,11 @@ use const common\LATEST_RULES_VERSION;
 | 
			
		||||
 * @property string  $profileLink ссылка на профиль на Ely без поддержки static url (только для записи)
 | 
			
		||||
 *
 | 
			
		||||
 * Отношения:
 | 
			
		||||
 * @property EmailActivation[] $emailActivations
 | 
			
		||||
 * @property OauthSession[]    $oauthSessions
 | 
			
		||||
 * @property UsernameHistory[] $usernameHistory
 | 
			
		||||
 * @property AccountSession[]  $sessions
 | 
			
		||||
 * @property EmailActivation[]    $emailActivations
 | 
			
		||||
 * @property OauthSession[]       $oauthSessions
 | 
			
		||||
 * @property UsernameHistory[]    $usernameHistory
 | 
			
		||||
 * @property AccountSession[]     $sessions
 | 
			
		||||
 * @property MinecraftAccessKey[] $minecraftAccessKeys
 | 
			
		||||
 *
 | 
			
		||||
 * Поведения:
 | 
			
		||||
 * @mixin TimestampBehavior
 | 
			
		||||
@@ -99,7 +100,7 @@ class Account extends ActiveRecord {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getOauthSessions() {
 | 
			
		||||
        return $this->hasMany(OauthSession::class, ['owner_id' => 'id']);
 | 
			
		||||
        return $this->hasMany(OauthSession::class, ['owner_id' => 'id'])->andWhere(['owner_type' => 'user']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getUsernameHistory() {
 | 
			
		||||
@@ -110,6 +111,10 @@ class Account extends ActiveRecord {
 | 
			
		||||
        return $this->hasMany(AccountSession::class, ['account_id' => 'id']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getMinecraftAccessKeys() {
 | 
			
		||||
        return $this->hasMany(MinecraftAccessKey::class, ['account_id' => 'id']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Выполняет проверку, принадлежит ли этому нику аккаунт у Mojang
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,62 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace common\models;
 | 
			
		||||
 | 
			
		||||
use common\components\Annotations\Reader;
 | 
			
		||||
use ReflectionClass;
 | 
			
		||||
use Yii;
 | 
			
		||||
 | 
			
		||||
class OauthScope {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @owner user
 | 
			
		||||
     */
 | 
			
		||||
    const OFFLINE_ACCESS = 'offline_access';
 | 
			
		||||
    /**
 | 
			
		||||
     * @owner user
 | 
			
		||||
     */
 | 
			
		||||
    const MINECRAFT_SERVER_SESSION = 'minecraft_server_session';
 | 
			
		||||
    /**
 | 
			
		||||
     * @owner user
 | 
			
		||||
     */
 | 
			
		||||
    const ACCOUNT_INFO = 'account_info';
 | 
			
		||||
    /**
 | 
			
		||||
     * @owner user
 | 
			
		||||
     */
 | 
			
		||||
    const ACCOUNT_EMAIL = 'account_email';
 | 
			
		||||
    /**
 | 
			
		||||
     * @internal
 | 
			
		||||
     * @owner machine
 | 
			
		||||
     */
 | 
			
		||||
    const ACCOUNT_BLOCK = 'account_block';
 | 
			
		||||
 | 
			
		||||
    public static function getScopes() : array {
 | 
			
		||||
        return [
 | 
			
		||||
            self::OFFLINE_ACCESS,
 | 
			
		||||
            self::MINECRAFT_SERVER_SESSION,
 | 
			
		||||
            self::ACCOUNT_INFO,
 | 
			
		||||
            self::ACCOUNT_EMAIL,
 | 
			
		||||
        ];
 | 
			
		||||
    public static function find(): OauthScopeQuery {
 | 
			
		||||
        return new OauthScopeQuery(static::queryScopes());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static function queryScopes(): array {
 | 
			
		||||
        $cacheKey = 'oauth-scopes-list';
 | 
			
		||||
        $scopes = false;
 | 
			
		||||
        if ($scopes === false) {
 | 
			
		||||
            $scopes = [];
 | 
			
		||||
            $reflection = new ReflectionClass(static::class);
 | 
			
		||||
            $constants = $reflection->getConstants();
 | 
			
		||||
            $reader = Reader::createFromDefaults();
 | 
			
		||||
            foreach ($constants as $constName => $value) {
 | 
			
		||||
                $annotations = $reader->getConstantAnnotations(static::class, $constName);
 | 
			
		||||
                $isInternal = $annotations->get('internal', false);
 | 
			
		||||
                $owner = $annotations->get('owner', 'user');
 | 
			
		||||
                $keyValue = [
 | 
			
		||||
                    'value' => $value,
 | 
			
		||||
                    'internal' => $isInternal,
 | 
			
		||||
                    'owner' => $owner,
 | 
			
		||||
                ];
 | 
			
		||||
                $scopes[$constName] = $keyValue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Yii::$app->cache->set($cacheKey, $scopes, 3600);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $scopes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										50
									
								
								common/models/OauthScopeQuery.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								common/models/OauthScopeQuery.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace common\models;
 | 
			
		||||
 | 
			
		||||
use yii\helpers\ArrayHelper;
 | 
			
		||||
 | 
			
		||||
class OauthScopeQuery {
 | 
			
		||||
 | 
			
		||||
    private $scopes;
 | 
			
		||||
 | 
			
		||||
    private $internal;
 | 
			
		||||
 | 
			
		||||
    private $owner;
 | 
			
		||||
 | 
			
		||||
    public function onlyPublic(): self {
 | 
			
		||||
        $this->internal = false;
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function onlyInternal(): self {
 | 
			
		||||
        $this->internal = true;
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function usersScopes(): self {
 | 
			
		||||
        $this->owner = 'user';
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function machineScopes(): self {
 | 
			
		||||
        $this->owner = 'machine';
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function all(): array {
 | 
			
		||||
        return ArrayHelper::getColumn(array_filter($this->scopes, function($value) {
 | 
			
		||||
            $shouldCheckInternal = $this->internal !== null;
 | 
			
		||||
            $isInternalMatch = $value['internal'] === $this->internal;
 | 
			
		||||
            $shouldCheckOwner = $this->owner !== null;
 | 
			
		||||
            $isOwnerMatch = $value['owner'] === $this->owner;
 | 
			
		||||
 | 
			
		||||
            return (!$shouldCheckInternal || $isInternalMatch)
 | 
			
		||||
                && (!$shouldCheckOwner || $isOwnerMatch);
 | 
			
		||||
        }), 'value');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __construct(array $scopes) {
 | 
			
		||||
        $this->scopes = $scopes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								common/models/amqp/AccountBanned.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								common/models/amqp/AccountBanned.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace common\models\amqp;
 | 
			
		||||
 | 
			
		||||
use yii\base\Object;
 | 
			
		||||
 | 
			
		||||
class AccountBanned extends Object {
 | 
			
		||||
 | 
			
		||||
    public $accountId;
 | 
			
		||||
 | 
			
		||||
    public $duration = -1;
 | 
			
		||||
 | 
			
		||||
    public $message = '';
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								common/models/amqp/AccountPardoned.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								common/models/amqp/AccountPardoned.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace common\models\amqp;
 | 
			
		||||
 | 
			
		||||
use yii\base\Object;
 | 
			
		||||
 | 
			
		||||
class AccountPardoned extends Object {
 | 
			
		||||
 | 
			
		||||
    public $accountId;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -18,7 +18,7 @@
 | 
			
		||||
        "yiisoft/yii2": "2.0.10",
 | 
			
		||||
        "yiisoft/yii2-swiftmailer": "*",
 | 
			
		||||
        "ramsey/uuid": "^3.5.0",
 | 
			
		||||
        "league/oauth2-server": "dev-improvements#b9277ccd664dcb80a766b73674d21de686cb9dda",
 | 
			
		||||
        "league/oauth2-server": "dev-improvements#fbaa9b0bd3d8050235ba7dde90f731764122bc20",
 | 
			
		||||
        "yiisoft/yii2-redis": "~2.0.0",
 | 
			
		||||
        "guzzlehttp/guzzle": "^6.0.0",
 | 
			
		||||
        "php-amqplib/php-amqplib": "^2.6.2",
 | 
			
		||||
@@ -27,7 +27,8 @@
 | 
			
		||||
        "ely/amqp-controller": "dev-master#d7f8cdbc66c45e477c9c7d5d509bc0c1b11fd3ec",
 | 
			
		||||
        "ely/email-renderer": "dev-master#ef1cb3f7a13196524b97ca5aa0a2d5867f2d9207",
 | 
			
		||||
        "predis/predis": "^1.0",
 | 
			
		||||
        "mito/yii2-sentry": "dev-fix_init#27f00805cb906f73b2c6f8181c1c655decb9be70"
 | 
			
		||||
        "mito/yii2-sentry": "dev-fix_init#27f00805cb906f73b2c6f8181c1c655decb9be70",
 | 
			
		||||
        "minime/annotations": "~3.0"
 | 
			
		||||
    },
 | 
			
		||||
    "require-dev": {
 | 
			
		||||
        "yiisoft/yii2-codeception": "*",
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,13 @@ namespace console\controllers;
 | 
			
		||||
 | 
			
		||||
use common\components\Mojang\Api as MojangApi;
 | 
			
		||||
use common\components\Mojang\exceptions\NoContentException;
 | 
			
		||||
use common\models\Account;
 | 
			
		||||
use common\models\amqp\AccountBanned;
 | 
			
		||||
use common\models\amqp\UsernameChanged;
 | 
			
		||||
use common\models\MojangUsername;
 | 
			
		||||
use Ely\Amqp\Builder\Configurator;
 | 
			
		||||
use GuzzleHttp\Exception\RequestException;
 | 
			
		||||
use Yii;
 | 
			
		||||
 | 
			
		||||
class AccountQueueController extends AmqpController {
 | 
			
		||||
 | 
			
		||||
@@ -17,16 +20,18 @@ class AccountQueueController extends AmqpController {
 | 
			
		||||
    public function configure(Configurator $configurator) {
 | 
			
		||||
        $configurator->exchange->topic()->durable();
 | 
			
		||||
        $configurator->queue->name('accounts-accounts-events')->durable();
 | 
			
		||||
        $configurator->bind->routingKey('accounts.username-changed');
 | 
			
		||||
        $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) {
 | 
			
		||||
    public function routeUsernameChanged(UsernameChanged $body): bool {
 | 
			
		||||
        $mojangApi = $this->createMojangApi();
 | 
			
		||||
        try {
 | 
			
		||||
            $response = $mojangApi->usernameToUUID($body->newUsername);
 | 
			
		||||
@@ -58,10 +63,32 @@ class AccountQueueController extends AmqpController {
 | 
			
		||||
        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 {
 | 
			
		||||
    protected function createMojangApi(): MojangApi {
 | 
			
		||||
        return new MojangApi();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,15 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use console\db\Migration;
 | 
			
		||||
 | 
			
		||||
class m161228_101022_oauth_clients_allow_null_redirect_uri extends Migration {
 | 
			
		||||
 | 
			
		||||
    public function safeUp() {
 | 
			
		||||
        $this->alterColumn('{{%oauth_clients}}', 'redirect_uri', $this->string());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function safeDown() {
 | 
			
		||||
        $this->alterColumn('{{%oauth_clients}}', 'redirect_uri', $this->string()->notNull());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								tests/codeception/api/_pages/InternalRoute.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tests/codeception/api/_pages/InternalRoute.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace tests\codeception\api\_pages;
 | 
			
		||||
 | 
			
		||||
use yii\codeception\BasePage;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @property \tests\codeception\api\FunctionalTester $actor
 | 
			
		||||
 */
 | 
			
		||||
class InternalRoute extends BasePage {
 | 
			
		||||
 | 
			
		||||
    public function ban($accountId) {
 | 
			
		||||
        $this->route = '/internal/accounts/' . $accountId . '/ban';
 | 
			
		||||
        $this->actor->sendPOST($this->getUrl());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function pardon($accountId) {
 | 
			
		||||
        $this->route = '/internal/accounts/' . $accountId . '/ban';
 | 
			
		||||
        $this->actor->sendDELETE($this->getUrl());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -281,6 +281,21 @@ class OauthAuthCodeCest {
 | 
			
		||||
            'statusCode' => 400,
 | 
			
		||||
        ]);
 | 
			
		||||
        $I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
 | 
			
		||||
 | 
			
		||||
        $I->wantTo('check behavior on request internal scope');
 | 
			
		||||
        $this->route->$action($this->buildQueryParams('ely', 'http://ely.by', 'code', [
 | 
			
		||||
            S::MINECRAFT_SERVER_SESSION,
 | 
			
		||||
            S::ACCOUNT_BLOCK,
 | 
			
		||||
        ]));
 | 
			
		||||
        $I->canSeeResponseCodeIs(400);
 | 
			
		||||
        $I->canSeeResponseIsJson();
 | 
			
		||||
        $I->canSeeResponseContainsJson([
 | 
			
		||||
            'success' => false,
 | 
			
		||||
            'error' => 'invalid_scope',
 | 
			
		||||
            'parameter' => S::ACCOUNT_BLOCK,
 | 
			
		||||
            'statusCode' => 400,
 | 
			
		||||
        ]);
 | 
			
		||||
        $I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,120 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace tests\codeception\api;
 | 
			
		||||
 | 
			
		||||
use common\models\OauthScope as S;
 | 
			
		||||
use tests\codeception\api\_pages\OauthRoute;
 | 
			
		||||
use tests\codeception\api\functional\_steps\OauthSteps;
 | 
			
		||||
 | 
			
		||||
class OauthClientCredentialsGrantCest {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var OauthRoute
 | 
			
		||||
     */
 | 
			
		||||
    private $route;
 | 
			
		||||
 | 
			
		||||
    public function _before(FunctionalTester $I) {
 | 
			
		||||
        $this->route = new OauthRoute($I);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testIssueTokenWithWrongArgs(FunctionalTester $I) {
 | 
			
		||||
        $I->wantTo('check behavior on on request without any credentials');
 | 
			
		||||
        $this->route->issueToken($this->buildParams());
 | 
			
		||||
        $I->canSeeResponseCodeIs(400);
 | 
			
		||||
        $I->canSeeResponseContainsJson([
 | 
			
		||||
            'error' => 'invalid_request',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $I->wantTo('check behavior on passing invalid client_id');
 | 
			
		||||
        $this->route->issueToken($this->buildParams(
 | 
			
		||||
            'invalid-client',
 | 
			
		||||
            'invalid-secret',
 | 
			
		||||
            ['invalid-scope']
 | 
			
		||||
        ));
 | 
			
		||||
        $I->canSeeResponseCodeIs(401);
 | 
			
		||||
        $I->canSeeResponseContainsJson([
 | 
			
		||||
            'error' => 'invalid_client',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $I->wantTo('check behavior on passing invalid client_secret');
 | 
			
		||||
        $this->route->issueToken($this->buildParams(
 | 
			
		||||
            'ely',
 | 
			
		||||
            'invalid-secret',
 | 
			
		||||
            ['invalid-scope']
 | 
			
		||||
        ));
 | 
			
		||||
        $I->canSeeResponseCodeIs(401);
 | 
			
		||||
        $I->canSeeResponseContainsJson([
 | 
			
		||||
            'error' => 'invalid_client',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $I->wantTo('check behavior on passing invalid client_secret');
 | 
			
		||||
        $this->route->issueToken($this->buildParams(
 | 
			
		||||
            'ely',
 | 
			
		||||
            'invalid-secret',
 | 
			
		||||
            ['invalid-scope']
 | 
			
		||||
        ));
 | 
			
		||||
        $I->canSeeResponseCodeIs(401);
 | 
			
		||||
        $I->canSeeResponseContainsJson([
 | 
			
		||||
            'error' => 'invalid_client',
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testIssueTokenWithPublicScopes(OauthSteps $I) {
 | 
			
		||||
        // TODO: у нас пока нет публичных скоупов, поэтому тест прогоняется с пустым набором
 | 
			
		||||
        $this->route->issueToken($this->buildParams(
 | 
			
		||||
            'ely',
 | 
			
		||||
            'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
 | 
			
		||||
            []
 | 
			
		||||
        ));
 | 
			
		||||
        $I->canSeeResponseCodeIs(200);
 | 
			
		||||
        $I->canSeeResponseIsJson();
 | 
			
		||||
        $I->canSeeResponseContainsJson([
 | 
			
		||||
            'token_type' => 'Bearer',
 | 
			
		||||
        ]);
 | 
			
		||||
        $I->canSeeResponseJsonMatchesJsonPath('$.access_token');
 | 
			
		||||
        $I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testIssueTokenWithInternalScopes(OauthSteps $I) {
 | 
			
		||||
        $this->route->issueToken($this->buildParams(
 | 
			
		||||
            'ely',
 | 
			
		||||
            'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
 | 
			
		||||
            [S::ACCOUNT_BLOCK]
 | 
			
		||||
        ));
 | 
			
		||||
        $I->canSeeResponseCodeIs(400);
 | 
			
		||||
        $I->canSeeResponseIsJson();
 | 
			
		||||
        $I->canSeeResponseContainsJson([
 | 
			
		||||
            'error' => 'invalid_scope',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $this->route->issueToken($this->buildParams(
 | 
			
		||||
            'trusted-client',
 | 
			
		||||
            'tXBbyvMcyaOgHMOAXBpN2EC7uFoJAaL9',
 | 
			
		||||
            [S::ACCOUNT_BLOCK]
 | 
			
		||||
        ));
 | 
			
		||||
        $I->canSeeResponseCodeIs(200);
 | 
			
		||||
        $I->canSeeResponseIsJson();
 | 
			
		||||
        $I->canSeeResponseContainsJson([
 | 
			
		||||
            'token_type' => 'Bearer',
 | 
			
		||||
        ]);
 | 
			
		||||
        $I->canSeeResponseJsonMatchesJsonPath('$.access_token');
 | 
			
		||||
        $I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function buildParams($clientId = null, $clientSecret = null, array $scopes = null) {
 | 
			
		||||
        $params = ['grant_type' => 'client_credentials'];
 | 
			
		||||
        if ($clientId !== null) {
 | 
			
		||||
            $params['client_id'] = $clientId;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($clientSecret !== null) {
 | 
			
		||||
            $params['client_secret'] = $clientSecret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($scopes !== null) {
 | 
			
		||||
            $params['scope'] = implode(',', $scopes);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $params;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -3,11 +3,12 @@ namespace tests\codeception\api\functional\_steps;
 | 
			
		||||
 | 
			
		||||
use common\models\OauthScope as S;
 | 
			
		||||
use tests\codeception\api\_pages\OauthRoute;
 | 
			
		||||
use tests\codeception\api\FunctionalTester;
 | 
			
		||||
 | 
			
		||||
class OauthSteps extends \tests\codeception\api\FunctionalTester {
 | 
			
		||||
class OauthSteps extends FunctionalTester {
 | 
			
		||||
 | 
			
		||||
    public function getAuthCode(array $permissions = []) {
 | 
			
		||||
        // TODO: по идее можно напрямую сделать зпись в базу, что ускорит процесс тестирования
 | 
			
		||||
        // TODO: по идее можно напрямую сделать запись в базу, что ускорит процесс тестирования
 | 
			
		||||
        $this->loggedInAsActiveAccount();
 | 
			
		||||
        $route = new OauthRoute($this);
 | 
			
		||||
        $route->complete([
 | 
			
		||||
@@ -31,7 +32,7 @@ class OauthSteps extends \tests\codeception\api\FunctionalTester {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getRefreshToken(array $permissions = []) {
 | 
			
		||||
        // TODO: по идее можно напрямую сделать зпись в базу, что ускорит процесс тестирования
 | 
			
		||||
        // TODO: по идее можно напрямую сделать запись в базу, что ускорит процесс тестирования
 | 
			
		||||
        $authCode = $this->getAuthCode(array_merge([S::OFFLINE_ACCESS], $permissions));
 | 
			
		||||
        $response = $this->issueToken($authCode);
 | 
			
		||||
 | 
			
		||||
@@ -51,4 +52,18 @@ class OauthSteps extends \tests\codeception\api\FunctionalTester {
 | 
			
		||||
        return json_decode($this->grabResponse(), true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getAccessTokenByClientCredentialsGrant(array $permissions = [], $useTrusted = true) {
 | 
			
		||||
        $route = new OauthRoute($this);
 | 
			
		||||
        $route->issueToken([
 | 
			
		||||
            'client_id' => $useTrusted ? 'trusted-client' : 'default-client',
 | 
			
		||||
            'client_secret' => $useTrusted ? 'tXBbyvMcyaOgHMOAXBpN2EC7uFoJAaL9' : 'AzWRy7ZjS1yRQUk2vRBDic8fprOKDB1W',
 | 
			
		||||
            'grant_type' => 'client_credentials',
 | 
			
		||||
            'scope' => implode(',', $permissions),
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $response = json_decode($this->grabResponse(), true);
 | 
			
		||||
 | 
			
		||||
        return $response['access_token'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								tests/codeception/api/functional/internal/BanCest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								tests/codeception/api/functional/internal/BanCest.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace tests\codeception\api\functional\internal;
 | 
			
		||||
 | 
			
		||||
use common\models\OauthScope as S;
 | 
			
		||||
use tests\codeception\api\_pages\InternalRoute;
 | 
			
		||||
use tests\codeception\api\functional\_steps\OauthSteps;
 | 
			
		||||
use tests\codeception\api\FunctionalTester;
 | 
			
		||||
 | 
			
		||||
class BanCest {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var InternalRoute
 | 
			
		||||
     */
 | 
			
		||||
    private $route;
 | 
			
		||||
 | 
			
		||||
    public function _before(FunctionalTester $I) {
 | 
			
		||||
        $this->route = new InternalRoute($I);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testBanAccount(OauthSteps $I) {
 | 
			
		||||
        $accessToken = $I->getAccessTokenByClientCredentialsGrant([S::ACCOUNT_BLOCK]);
 | 
			
		||||
        $I->amBearerAuthenticated($accessToken);
 | 
			
		||||
 | 
			
		||||
        $this->route->ban(1);
 | 
			
		||||
        $I->canSeeResponseCodeIs(200);
 | 
			
		||||
        $I->canSeeResponseIsJson();
 | 
			
		||||
        $I->canSeeResponseContainsJson([
 | 
			
		||||
            'success' => true,
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testBanBannedAccount(OauthSteps $I) {
 | 
			
		||||
        $accessToken = $I->getAccessTokenByClientCredentialsGrant([S::ACCOUNT_BLOCK]);
 | 
			
		||||
        $I->amBearerAuthenticated($accessToken);
 | 
			
		||||
 | 
			
		||||
        $this->route->ban(10);
 | 
			
		||||
        $I->canSeeResponseCodeIs(200);
 | 
			
		||||
        $I->canSeeResponseIsJson();
 | 
			
		||||
        $I->canSeeResponseContainsJson([
 | 
			
		||||
            'success' => false,
 | 
			
		||||
            'errors' => [
 | 
			
		||||
                'account' => 'error.account_already_banned',
 | 
			
		||||
            ],
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								tests/codeception/api/functional/internal/PardonCest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								tests/codeception/api/functional/internal/PardonCest.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace tests\codeception\api\functional\internal;
 | 
			
		||||
 | 
			
		||||
use common\models\OauthScope as S;
 | 
			
		||||
use tests\codeception\api\_pages\InternalRoute;
 | 
			
		||||
use tests\codeception\api\functional\_steps\OauthSteps;
 | 
			
		||||
use tests\codeception\api\FunctionalTester;
 | 
			
		||||
 | 
			
		||||
class PardonCest {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var InternalRoute
 | 
			
		||||
     */
 | 
			
		||||
    private $route;
 | 
			
		||||
 | 
			
		||||
    public function _before(FunctionalTester $I) {
 | 
			
		||||
        $this->route = new InternalRoute($I);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testPardonAccount(OauthSteps $I) {
 | 
			
		||||
        $accessToken = $I->getAccessTokenByClientCredentialsGrant([S::ACCOUNT_BLOCK]);
 | 
			
		||||
        $I->amBearerAuthenticated($accessToken);
 | 
			
		||||
 | 
			
		||||
        $this->route->pardon(10);
 | 
			
		||||
        $I->canSeeResponseCodeIs(200);
 | 
			
		||||
        $I->canSeeResponseIsJson();
 | 
			
		||||
        $I->canSeeResponseContainsJson([
 | 
			
		||||
            'success' => true,
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testPardonNotBannedAccount(OauthSteps $I) {
 | 
			
		||||
        $accessToken = $I->getAccessTokenByClientCredentialsGrant([S::ACCOUNT_BLOCK]);
 | 
			
		||||
        $I->amBearerAuthenticated($accessToken);
 | 
			
		||||
 | 
			
		||||
        $this->route->pardon(1);
 | 
			
		||||
        $I->canSeeResponseCodeIs(200);
 | 
			
		||||
        $I->canSeeResponseIsJson();
 | 
			
		||||
        $I->canSeeResponseContainsJson([
 | 
			
		||||
            'success' => false,
 | 
			
		||||
            'errors' => [
 | 
			
		||||
                'account' => 'error.account_not_banned',
 | 
			
		||||
            ],
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,62 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace tests\codeception\api\unit\modules\internal\models;
 | 
			
		||||
 | 
			
		||||
use api\modules\internal\helpers\Error as E;
 | 
			
		||||
use api\modules\internal\models\BanForm;
 | 
			
		||||
use common\models\Account;
 | 
			
		||||
use tests\codeception\api\unit\TestCase;
 | 
			
		||||
 | 
			
		||||
class BanFormTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
    public function testValidateAccountActivity() {
 | 
			
		||||
        $account = new Account();
 | 
			
		||||
        $account->status = Account::STATUS_ACTIVE;
 | 
			
		||||
        $form = new BanForm($account);
 | 
			
		||||
        $form->validateAccountActivity();
 | 
			
		||||
        $this->assertEmpty($form->getErrors('account'));
 | 
			
		||||
 | 
			
		||||
        $account = new Account();
 | 
			
		||||
        $account->status = Account::STATUS_BANNED;
 | 
			
		||||
        $form = new BanForm($account);
 | 
			
		||||
        $form->validateAccountActivity();
 | 
			
		||||
        $this->assertEquals([E::ACCOUNT_ALREADY_BANNED], $form->getErrors('account'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testBan() {
 | 
			
		||||
        /** @var Account|\PHPUnit_Framework_MockObject_MockObject $account */
 | 
			
		||||
        $account = $this->getMockBuilder(Account::class)
 | 
			
		||||
            ->setMethods(['save'])
 | 
			
		||||
            ->getMock();
 | 
			
		||||
 | 
			
		||||
        $account->expects($this->once())
 | 
			
		||||
            ->method('save')
 | 
			
		||||
            ->willReturn(true);
 | 
			
		||||
 | 
			
		||||
        $model = new BanForm($account);
 | 
			
		||||
        $this->assertTrue($model->ban());
 | 
			
		||||
        $this->assertEquals(Account::STATUS_BANNED, $account->status);
 | 
			
		||||
        $this->tester->canSeeAmqpMessageIsCreated('events');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testCreateTask() {
 | 
			
		||||
        $account = new Account();
 | 
			
		||||
        $account->id = 3;
 | 
			
		||||
 | 
			
		||||
        $model = new BanForm($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 BanForm($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']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,52 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace tests\codeception\api\unit\modules\internal\models;
 | 
			
		||||
 | 
			
		||||
use api\modules\internal\helpers\Error as E;
 | 
			
		||||
use api\modules\internal\models\PardonForm;
 | 
			
		||||
use common\models\Account;
 | 
			
		||||
use tests\codeception\api\unit\TestCase;
 | 
			
		||||
 | 
			
		||||
class PardonFormTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
    public function testValidateAccountBanned() {
 | 
			
		||||
        $account = new Account();
 | 
			
		||||
        $account->status = Account::STATUS_BANNED;
 | 
			
		||||
        $form = new PardonForm($account);
 | 
			
		||||
        $form->validateAccountBanned();
 | 
			
		||||
        $this->assertEmpty($form->getErrors('account'));
 | 
			
		||||
 | 
			
		||||
        $account = new Account();
 | 
			
		||||
        $account->status = Account::STATUS_ACTIVE;
 | 
			
		||||
        $form = new PardonForm($account);
 | 
			
		||||
        $form->validateAccountBanned();
 | 
			
		||||
        $this->assertEquals([E::ACCOUNT_NOT_BANNED], $form->getErrors('account'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testPardon() {
 | 
			
		||||
        /** @var Account|\PHPUnit_Framework_MockObject_MockObject $account */
 | 
			
		||||
        $account = $this->getMockBuilder(Account::class)
 | 
			
		||||
            ->setMethods(['save'])
 | 
			
		||||
            ->getMock();
 | 
			
		||||
 | 
			
		||||
        $account->expects($this->once())
 | 
			
		||||
            ->method('save')
 | 
			
		||||
            ->willReturn(true);
 | 
			
		||||
 | 
			
		||||
        $account->status = Account::STATUS_BANNED;
 | 
			
		||||
        $model = new PardonForm($account);
 | 
			
		||||
        $this->assertTrue($model->pardon());
 | 
			
		||||
        $this->assertEquals(Account::STATUS_ACTIVE, $account->status);
 | 
			
		||||
        $this->tester->canSeeAmqpMessageIsCreated('events');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testCreateTask() {
 | 
			
		||||
        $account = new Account();
 | 
			
		||||
        $account->id = 3;
 | 
			
		||||
 | 
			
		||||
        $model = new PardonForm($account);
 | 
			
		||||
        $model->createTask();
 | 
			
		||||
        $message = json_decode($this->tester->grabLastSentAmqpMessage('events')->body, true);
 | 
			
		||||
        $this->assertSame(3, $message['accountId']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -16,4 +16,12 @@ return [
 | 
			
		||||
        'created_at' => time(),
 | 
			
		||||
        'last_refreshed_at' => time(),
 | 
			
		||||
    ],
 | 
			
		||||
    'banned-user-session' => [
 | 
			
		||||
        'id' => 3,
 | 
			
		||||
        'account_id' => 10,
 | 
			
		||||
        'refresh_token' => 'Af7fIuV6eL61tRUHn40yhmDRXN1OQxKR',
 | 
			
		||||
        'last_used_ip' => ip2long('182.123.234.123'),
 | 
			
		||||
        'created_at' => time(),
 | 
			
		||||
        'last_refreshed_at' => time(),
 | 
			
		||||
    ],
 | 
			
		||||
];
 | 
			
		||||
 
 | 
			
		||||
@@ -30,4 +30,24 @@ return [
 | 
			
		||||
        'is_trusted' => 0,
 | 
			
		||||
        'created_at' => 1479937982,
 | 
			
		||||
    ],
 | 
			
		||||
    'trustedClient' => [
 | 
			
		||||
        'id' => 'trusted-client',
 | 
			
		||||
        'secret' => 'tXBbyvMcyaOgHMOAXBpN2EC7uFoJAaL9',
 | 
			
		||||
        'name' => 'Trusted client',
 | 
			
		||||
        'description' => 'Это клиент, которому мы доверяем',
 | 
			
		||||
        'redirect_uri' => null,
 | 
			
		||||
        'account_id' => null,
 | 
			
		||||
        'is_trusted' => 1,
 | 
			
		||||
        'created_at' => 1482922663,
 | 
			
		||||
    ],
 | 
			
		||||
    'defaultClient' => [
 | 
			
		||||
        'id' => 'default-client',
 | 
			
		||||
        'secret' => 'AzWRy7ZjS1yRQUk2vRBDic8fprOKDB1W',
 | 
			
		||||
        'name' => 'Default client',
 | 
			
		||||
        'description' => 'Это обычный клиент, каких может быть много',
 | 
			
		||||
        'redirect_uri' => null,
 | 
			
		||||
        'account_id' => null,
 | 
			
		||||
        'is_trusted' => 0,
 | 
			
		||||
        'created_at' => 1482922711,
 | 
			
		||||
    ],
 | 
			
		||||
];
 | 
			
		||||
 
 | 
			
		||||
@@ -7,4 +7,11 @@ return [
 | 
			
		||||
        'client_id' => 'test1',
 | 
			
		||||
        'client_redirect_uri' => 'http://test1.net/oauth',
 | 
			
		||||
    ],
 | 
			
		||||
    'banned-account-session' => [
 | 
			
		||||
        'id' => 2,
 | 
			
		||||
        'owner_type' => 'user',
 | 
			
		||||
        'owner_id' => 10,
 | 
			
		||||
        'client_id' => 'test1',
 | 
			
		||||
        'client_redirect_uri' => 'http://test1.net/oauth',
 | 
			
		||||
    ],
 | 
			
		||||
];
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ namespace codeception\console\unit\controllers;
 | 
			
		||||
use common\components\Mojang\Api;
 | 
			
		||||
use common\components\Mojang\exceptions\NoContentException;
 | 
			
		||||
use common\components\Mojang\response\UsernameToUUIDResponse;
 | 
			
		||||
use common\models\amqp\AccountBanned;
 | 
			
		||||
use common\models\amqp\UsernameChanged;
 | 
			
		||||
use common\models\MojangUsername;
 | 
			
		||||
use console\controllers\AccountQueueController;
 | 
			
		||||
@@ -143,4 +144,22 @@ class AccountQueueControllerTest extends TestCase {
 | 
			
		||||
        $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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user