mirror of
				https://github.com/elyby/accounts.git
				synced 2025-05-31 14:11:46 +05:30 
			
		
		
		
	Fixed almost everything, but all functional tests are broken at the last minute :(
This commit is contained in:
		| @@ -22,4 +22,5 @@ coverage: | ||||
|       - tests/* | ||||
|       - codeception.dist.yml | ||||
|       - codeception.yml | ||||
|       - index.php | ||||
|   c3url: 'http://localhost/api/web/index.php' | ||||
|   | ||||
| @@ -3,8 +3,8 @@ namespace api\components\OAuth2\Storage; | ||||
|  | ||||
| use api\components\OAuth2\Entities\ClientEntity; | ||||
| use api\components\OAuth2\Entities\ScopeEntity; | ||||
| use api\rbac\Permissions as P; | ||||
| use Assert\Assert; | ||||
| use common\rbac\Permissions as P; | ||||
| use League\OAuth2\Server\Storage\AbstractStorage; | ||||
| use League\OAuth2\Server\Storage\ScopeInterface; | ||||
|  | ||||
|   | ||||
| @@ -3,6 +3,7 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace api\components\Tokens; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Exception; | ||||
| use Lcobucci\JWT\Builder; | ||||
| use Lcobucci\JWT\Parser; | ||||
| @@ -11,8 +12,6 @@ use yii\base\Component as BaseComponent; | ||||
|  | ||||
| class Component extends BaseComponent { | ||||
|  | ||||
|     private const EXPIRATION_TIMEOUT = 3600; // 1h | ||||
|  | ||||
|     private const PREFERRED_ALGORITHM = 'ES256'; | ||||
|  | ||||
|     /** | ||||
| @@ -41,10 +40,10 @@ class Component extends BaseComponent { | ||||
|     private $algorithmManager; | ||||
|  | ||||
|     public function create(array $payloads = [], array $headers = []): Token { | ||||
|         $time = time(); | ||||
|         $now = Carbon::now(); | ||||
|         $builder = (new Builder()) | ||||
|             ->issuedAt($time) | ||||
|             ->expiresAt($time + self::EXPIRATION_TIMEOUT); | ||||
|             ->issuedAt($now->getTimestamp()) | ||||
|             ->expiresAt($now->addHour()->getTimestamp()); | ||||
|         foreach ($payloads as $claim => $value) { | ||||
|             $builder->withClaim($claim, $value); | ||||
|         } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace api\components\Tokens; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use common\models\Account; | ||||
| use common\models\AccountSession; | ||||
| use Lcobucci\JWT\Token; | ||||
| @@ -20,7 +21,7 @@ class TokensFactory { | ||||
|         if ($session === null) { | ||||
|             // If we don't remember a session, the token should live longer | ||||
|             // so that the session doesn't end while working with the account | ||||
|             $payloads['exp'] = time() + 60 * 60 * 24 * 7; // 7d | ||||
|             $payloads['exp'] = Carbon::now()->addDays(7)->getTimestamp(); | ||||
|         } else { | ||||
|             $payloads['jti'] = $session->id; | ||||
|         } | ||||
|   | ||||
| @@ -5,10 +5,6 @@ namespace api\components\User; | ||||
|  | ||||
| use common\models\Account; | ||||
| use common\models\AccountSession; | ||||
| use Exception; | ||||
| use InvalidArgumentException; | ||||
| use Yii; | ||||
| use yii\web\UnauthorizedHttpException; | ||||
| use yii\web\User as YiiUserComponent; | ||||
|  | ||||
| /** | ||||
| @@ -38,29 +34,11 @@ class Component extends YiiUserComponent { | ||||
|      */ | ||||
|     public $identityClass = IdentityFactory::class; | ||||
|  | ||||
|     public function findIdentityByAccessToken($accessToken): ?IdentityInterface { | ||||
|         if (empty($accessToken)) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             return IdentityFactory::findIdentityByAccessToken($accessToken); | ||||
|         } catch (UnauthorizedHttpException $e) { | ||||
|             // TODO: if this exception is catched there, how it forms "Token expired" exception? | ||||
|             // Do nothing. It's okay to catch this. | ||||
|         } catch (Exception $e) { | ||||
|             Yii::error($e); | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The method searches AccountSession model, which one has been used to create current JWT token. | ||||
|      * null will be returned in case when any of the following situations occurred: | ||||
|      * - The user isn't authorized | ||||
|      * - There is no header with a token | ||||
|      * - Token validation isn't passed and some exception has been thrown | ||||
|      * - The user isn't authorized via JWT token | ||||
|      * - No session key found in the token. This is possible if the user chose not to remember me | ||||
|      *   or just some old tokens, without the support of saving the used session | ||||
|      * | ||||
| @@ -71,18 +49,13 @@ class Component extends YiiUserComponent { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         $bearer = $this->getBearerToken(); | ||||
|         if ($bearer === null) { | ||||
|         /** @var IdentityInterface $identity */ | ||||
|         $identity = $this->getIdentity(); | ||||
|         if (!$identity instanceof JwtIdentity) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             $token = Yii::$app->tokens->parse($bearer); | ||||
|         } catch (InvalidArgumentException $e) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         $sessionId = $token->getClaim('jti', false); | ||||
|         $sessionId = $identity->getToken()->getClaim('jti', false); | ||||
|         if ($sessionId === false) { | ||||
|             return null; | ||||
|         } | ||||
| @@ -111,13 +84,4 @@ class Component extends YiiUserComponent { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function getBearerToken(): ?string { | ||||
|         $authHeader = Yii::$app->request->getHeaders()->get('Authorization'); | ||||
|         if ($authHeader === null || !preg_match('/^Bearer\s+(.*?)$/', $authHeader, $matches)) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         return $matches[1]; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ declare(strict_types=1); | ||||
| namespace api\components\User; | ||||
|  | ||||
| use api\components\Tokens\TokensFactory; | ||||
| use Carbon\Carbon; | ||||
| use common\models\Account; | ||||
| use Exception; | ||||
| use Lcobucci\JWT\Token; | ||||
| @@ -36,22 +37,27 @@ class JwtIdentity implements IdentityInterface { | ||||
|             throw new UnauthorizedHttpException('Incorrect token'); | ||||
|         } | ||||
|  | ||||
|         if ($token->isExpired()) { | ||||
|         $now = Carbon::now(); | ||||
|         if ($token->isExpired($now)) { | ||||
|             throw new UnauthorizedHttpException('Token expired'); | ||||
|         } | ||||
|  | ||||
|         if (!$token->validate(new ValidationData())) { | ||||
|         if (!$token->validate(new ValidationData($now->getTimestamp()))) { | ||||
|             throw new UnauthorizedHttpException('Incorrect token'); | ||||
|         } | ||||
|  | ||||
|         $sub = $token->getClaim('sub', false); | ||||
|         if ($sub !== false && strpos($sub, TokensFactory::SUB_ACCOUNT_PREFIX) !== 0) { | ||||
|         if ($sub !== false && strpos((string)$sub, TokensFactory::SUB_ACCOUNT_PREFIX) !== 0) { | ||||
|             throw new UnauthorizedHttpException('Incorrect token'); | ||||
|         } | ||||
|  | ||||
|         return new self($token); | ||||
|     } | ||||
|  | ||||
|     public function getToken(): Token { | ||||
|         return $this->token; | ||||
|     } | ||||
|  | ||||
|     public function getAccount(): ?Account { | ||||
|         $subject = $this->token->getClaim('sub', false); | ||||
|         if ($subject === false) { | ||||
| @@ -77,6 +83,7 @@ class JwtIdentity implements IdentityInterface { | ||||
|         return (string)$this->token; | ||||
|     } | ||||
|  | ||||
|     // @codeCoverageIgnoreStart | ||||
|     public function getAuthKey() { | ||||
|         throw new NotSupportedException('This method used for cookie auth, except we using Bearer auth'); | ||||
|     } | ||||
| @@ -89,4 +96,6 @@ class JwtIdentity implements IdentityInterface { | ||||
|         throw new NotSupportedException('This method used for cookie auth, except we using Bearer auth'); | ||||
|     } | ||||
|  | ||||
|     // @codeCoverageIgnoreEnd | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -5,8 +5,8 @@ use api\controllers\Controller; | ||||
| use api\modules\accounts\actions; | ||||
| use api\modules\accounts\models\AccountInfo; | ||||
| use api\modules\accounts\models\TwoFactorAuthInfo; | ||||
| use api\rbac\Permissions as P; | ||||
| use common\models\Account; | ||||
| use common\rbac\Permissions as P; | ||||
| use Yii; | ||||
| use yii\filters\AccessControl; | ||||
| use yii\helpers\ArrayHelper; | ||||
|   | ||||
| @@ -2,8 +2,8 @@ | ||||
| namespace api\modules\accounts\models; | ||||
|  | ||||
| use api\models\base\BaseAccountForm; | ||||
| use api\rbac\Permissions as P; | ||||
| use common\models\Account; | ||||
| use common\rbac\Permissions as P; | ||||
| use yii\di\Instance; | ||||
| use yii\web\User; | ||||
|  | ||||
|   | ||||
| @@ -2,8 +2,8 @@ | ||||
| namespace api\modules\internal\controllers; | ||||
|  | ||||
| use api\controllers\Controller; | ||||
| use api\rbac\Permissions as P; | ||||
| use common\models\Account; | ||||
| use common\rbac\Permissions as P; | ||||
| use yii\filters\AccessControl; | ||||
| use yii\helpers\ArrayHelper; | ||||
| use yii\web\BadRequestHttpException; | ||||
|   | ||||
| @@ -3,7 +3,7 @@ namespace api\modules\oauth\controllers; | ||||
|  | ||||
| use api\controllers\Controller; | ||||
| use api\modules\oauth\models\OauthProcess; | ||||
| use common\rbac\Permissions as P; | ||||
| use api\rbac\Permissions as P; | ||||
| use Yii; | ||||
| use yii\filters\AccessControl; | ||||
| use yii\helpers\ArrayHelper; | ||||
|   | ||||
| @@ -7,9 +7,9 @@ use api\modules\oauth\exceptions\UnsupportedOauthClientType; | ||||
| use api\modules\oauth\models\OauthClientForm; | ||||
| use api\modules\oauth\models\OauthClientFormFactory; | ||||
| use api\modules\oauth\models\OauthClientTypeForm; | ||||
| use api\rbac\Permissions as P; | ||||
| use common\models\Account; | ||||
| use common\models\OauthClient; | ||||
| use common\rbac\Permissions as P; | ||||
| use Yii; | ||||
| use yii\filters\AccessControl; | ||||
| use yii\helpers\ArrayHelper; | ||||
|   | ||||
| @@ -3,7 +3,7 @@ namespace api\modules\oauth\controllers; | ||||
|  | ||||
| use api\controllers\Controller; | ||||
| use api\modules\oauth\models\IdentityInfo; | ||||
| use common\rbac\Permissions as P; | ||||
| use api\rbac\Permissions as P; | ||||
| use Yii; | ||||
| use yii\filters\AccessControl; | ||||
| use yii\helpers\ArrayHelper; | ||||
|   | ||||
| @@ -5,9 +5,9 @@ use api\components\OAuth2\Exception\AcceptRequiredException; | ||||
| use api\components\OAuth2\Exception\AccessDeniedException; | ||||
| use api\components\OAuth2\Grants\AuthCodeGrant; | ||||
| use api\components\OAuth2\Grants\AuthorizeParams; | ||||
| use api\rbac\Permissions as P; | ||||
| use common\models\Account; | ||||
| use common\models\OauthClient; | ||||
| use common\rbac\Permissions as P; | ||||
| use League\OAuth2\Server\AuthorizationServer; | ||||
| use League\OAuth2\Server\Exception\InvalidGrantException; | ||||
| use League\OAuth2\Server\Exception\OAuthException; | ||||
|   | ||||
| @@ -6,10 +6,10 @@ use api\modules\session\exceptions\IllegalArgumentException; | ||||
| use api\modules\session\models\protocols\JoinInterface; | ||||
| use api\modules\session\Module as Session; | ||||
| use api\modules\session\validators\RequiredValidator; | ||||
| use api\rbac\Permissions as P; | ||||
| use common\helpers\StringHelper; | ||||
| use common\models\Account; | ||||
| use common\models\MinecraftAccessKey; | ||||
| use common\rbac\Permissions as P; | ||||
| use Ramsey\Uuid\Uuid; | ||||
| use Yii; | ||||
| use yii\base\ErrorException; | ||||
|   | ||||
							
								
								
									
										38
									
								
								api/rbac/Manager.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								api/rbac/Manager.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace api\rbac; | ||||
|  | ||||
| use Yii; | ||||
| use yii\rbac\Assignment; | ||||
| use yii\rbac\PhpManager; | ||||
|  | ||||
| class Manager extends PhpManager { | ||||
|  | ||||
|     /** | ||||
|      * In our application the permissions are given not to users itself, but to tokens, | ||||
|      * so we extract them from the extended identity interface. | ||||
|      * | ||||
|      * In Yii2, the mechanism of recursive permissions checking requires that the array | ||||
|      * with permissions must be indexed by the keys of these permissions. | ||||
|      * | ||||
|      * @param string $accessToken | ||||
|      * @return string[] | ||||
|      */ | ||||
|     public function getAssignments($accessToken): array { | ||||
|         $identity = Yii::$app->user->getIdentity(); | ||||
|         if ($identity === null) { | ||||
|             return []; | ||||
|         } | ||||
|  | ||||
|         /** @noinspection NullPointerExceptionInspection */ | ||||
|         $rawPermissions = $identity->getAssignedPermissions(); | ||||
|         $result = []; | ||||
|         foreach ($rawPermissions as $name) { | ||||
|             $result[$name] = new Assignment(['roleName' => $name]); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,5 +1,7 @@ | ||||
| <?php | ||||
| namespace common\rbac; | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace api\rbac; | ||||
| 
 | ||||
| final class Permissions { | ||||
| 
 | ||||
| @@ -1,5 +1,7 @@ | ||||
| <?php | ||||
| namespace common\rbac; | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace api\rbac; | ||||
| 
 | ||||
| final class Roles { | ||||
| 
 | ||||
| @@ -1,7 +1,10 @@ | ||||
| <?php | ||||
| namespace common\rbac\rules; | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace api\rbac\rules; | ||||
| 
 | ||||
| use common\models\Account; | ||||
| use Webmozart\Assert\Assert; | ||||
| use Yii; | ||||
| use yii\rbac\Rule; | ||||
| 
 | ||||
| @@ -23,12 +26,10 @@ class AccountOwner extends Rule { | ||||
|      * @return bool a value indicating whether the rule permits the auth item it is associated with. | ||||
|      */ | ||||
|     public function execute($accessToken, $item, $params): bool { | ||||
|         Assert::keyExists($params, 'accountId'); | ||||
|         $accountId = $params['accountId'] ?? null; | ||||
|         if ($accountId === null) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         $identity = Yii::$app->user->findIdentityByAccessToken($accessToken); | ||||
|         $identity = Yii::$app->user->getIdentity(); | ||||
|         if ($identity === null) { | ||||
|             return false; | ||||
|         } | ||||
| @@ -1,10 +1,11 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace common\rbac\rules; | ||||
| namespace api\rbac\rules; | ||||
| 
 | ||||
| use api\rbac\Permissions as P; | ||||
| use common\models\OauthClient; | ||||
| use common\rbac\Permissions as P; | ||||
| use Webmozart\Assert\Assert; | ||||
| use Yii; | ||||
| use yii\rbac\Rule; | ||||
| 
 | ||||
| @@ -30,18 +31,14 @@ class OauthClientOwner extends Rule { | ||||
|             return (new AccountOwner())->execute($accessToken, $item, ['accountId' => $accountId]); | ||||
|         } | ||||
| 
 | ||||
|         $clientId = $params['clientId'] ?? null; | ||||
|         if ($clientId === null) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         Assert::keyExists($params, 'clientId'); | ||||
|         /** @var OauthClient|null $client */ | ||||
|         $client = OauthClient::findOne($clientId); | ||||
|         $client = OauthClient::findOne(['id' => $params['clientId']]); | ||||
|         if ($client === null) { | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         $identity = Yii::$app->user->findIdentityByAccessToken($accessToken); | ||||
|         $identity = Yii::$app->user->getIdentity(); | ||||
|         if ($identity === null) { | ||||
|             return false; | ||||
|         } | ||||
| @@ -1,9 +1,9 @@ | ||||
| <?php | ||||
| namespace api\tests\functional\_steps; | ||||
|  | ||||
| use api\rbac\Permissions as P; | ||||
| use api\tests\_pages\SessionServerRoute; | ||||
| use api\tests\FunctionalTester; | ||||
| use common\rbac\Permissions as P; | ||||
| use Faker\Provider\Uuid; | ||||
|  | ||||
| class SessionServerSteps extends FunctionalTester { | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| <?php | ||||
| namespace api\tests\functional\accounts; | ||||
|  | ||||
| use api\rbac\Permissions as P; | ||||
| use api\tests\_pages\AccountsRoute; | ||||
| use api\tests\functional\_steps\OauthSteps; | ||||
| use api\tests\FunctionalTester; | ||||
| use common\rbac\Permissions as P; | ||||
|  | ||||
| class BanCest { | ||||
|  | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| <?php | ||||
| namespace api\tests\functional\accounts; | ||||
|  | ||||
| use api\rbac\Permissions as P; | ||||
| use api\tests\_pages\AccountsRoute; | ||||
| use api\tests\functional\_steps\OauthSteps; | ||||
| use api\tests\FunctionalTester; | ||||
| use common\rbac\Permissions as P; | ||||
|  | ||||
| class PardonCest { | ||||
|  | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| <?php | ||||
| namespace api\tests\functional\oauth; | ||||
|  | ||||
| use api\rbac\Permissions as P; | ||||
| use api\tests\_pages\OauthRoute; | ||||
| use api\tests\FunctionalTester; | ||||
| use common\rbac\Permissions as P; | ||||
|  | ||||
| class AuthCodeCest { | ||||
|  | ||||
|   | ||||
| @@ -2,10 +2,10 @@ | ||||
| namespace api\tests\functional\oauth; | ||||
|  | ||||
| use api\components\OAuth2\Storage\ScopeStorage as S; | ||||
| use api\rbac\Permissions as P; | ||||
| use api\tests\_pages\OauthRoute; | ||||
| use api\tests\functional\_steps\OauthSteps; | ||||
| use api\tests\FunctionalTester; | ||||
| use common\rbac\Permissions as P; | ||||
|  | ||||
| class RefreshTokenCest { | ||||
|  | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| <?php | ||||
| namespace api\tests\functional\sessionserver; | ||||
|  | ||||
| use api\rbac\Permissions as P; | ||||
| use api\tests\_pages\SessionServerRoute; | ||||
| use api\tests\functional\_steps\AuthserverSteps; | ||||
| use api\tests\functional\_steps\OauthSteps; | ||||
| use api\tests\FunctionalTester; | ||||
| use common\rbac\Permissions as P; | ||||
| use Faker\Provider\Uuid; | ||||
|  | ||||
| class JoinCest { | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| <?php | ||||
| namespace api\tests\functional\sessionserver; | ||||
|  | ||||
| use api\rbac\Permissions as P; | ||||
| use api\tests\_pages\SessionServerRoute; | ||||
| use api\tests\functional\_steps\AuthserverSteps; | ||||
| use api\tests\functional\_steps\OauthSteps; | ||||
| use api\tests\FunctionalTester; | ||||
| use common\rbac\Permissions as P; | ||||
| use Faker\Provider\Uuid; | ||||
|  | ||||
| class JoinLegacyCest { | ||||
|   | ||||
| @@ -16,11 +16,6 @@ class ComponentTest extends TestCase { | ||||
|      */ | ||||
|     private $component; | ||||
|  | ||||
|     protected function _setUp() { | ||||
|         parent::_setUp(); | ||||
|         $this->component = Yii::$app->tokens; | ||||
|     } | ||||
|  | ||||
|     public function testCreate() { | ||||
|         // Run without any arguments | ||||
|         $token = $this->component->create(); | ||||
| @@ -80,6 +75,11 @@ class ComponentTest extends TestCase { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     protected function _setUp() { | ||||
|         parent::_setUp(); | ||||
|         $this->component = Yii::$app->tokens; | ||||
|     } | ||||
|  | ||||
|     private function assertValidParsedToken(Token $token, string $expectedAlg) { | ||||
|         $this->assertSame($expectedAlg, $token->getHeader('alg')); | ||||
|         $this->assertSame(1564527476, $token->getClaim('iat')); | ||||
|   | ||||
| @@ -4,17 +4,16 @@ declare(strict_types=1); | ||||
| namespace codeception\api\unit\components\User; | ||||
|  | ||||
| use api\components\User\Component; | ||||
| use api\components\User\IdentityFactory; | ||||
| use api\components\User\JwtIdentity; | ||||
| use api\components\User\Oauth2Identity; | ||||
| use api\tests\unit\TestCase; | ||||
| use common\models\Account; | ||||
| use common\models\AccountSession; | ||||
| use common\tests\fixtures\AccountFixture; | ||||
| use common\tests\fixtures\AccountSessionFixture; | ||||
| use common\tests\fixtures\MinecraftAccessKeyFixture; | ||||
| use Emarref\Jwt\Claim; | ||||
| use Emarref\Jwt\Jwt; | ||||
| use Yii; | ||||
| use yii\web\Request; | ||||
| use Lcobucci\JWT\Claim\Basic; | ||||
| use Lcobucci\JWT\Token; | ||||
|  | ||||
| class ComponentTest extends TestCase { | ||||
|  | ||||
| @@ -36,53 +35,37 @@ class ComponentTest extends TestCase { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     // TODO: move test to refresh token form | ||||
|     public function testRenewJwtAuthenticationToken() { | ||||
|         $userIP = '192.168.0.1'; | ||||
|         $this->mockRequest($userIP); | ||||
|         /** @var AccountSession $session */ | ||||
|         $session = $this->tester->grabFixture('sessions', 'admin'); | ||||
|         $result = $this->component->renewJwtAuthenticationToken($session); | ||||
|         $this->assertSame($session, $result->getSession()); | ||||
|         $this->assertSame($session->account_id, $result->getAccount()->id); | ||||
|         $session->refresh(); // reload data from db | ||||
|         $this->assertEqualsWithDelta(time(), $session->last_refreshed_at, 3); | ||||
|         $this->assertSame($userIP, $session->getReadableIp()); | ||||
|         $payloads = (new Jwt())->deserialize($result->getJwt())->getPayload(); | ||||
|         /** @noinspection NullPointerExceptionInspection */ | ||||
|         $this->assertEqualsWithDelta(time(), $payloads->findClaimByName(Claim\IssuedAt::NAME)->getValue(), 3); | ||||
|         /** @noinspection NullPointerExceptionInspection */ | ||||
|         $this->assertEqualsWithDelta(time() + 3600, $payloads->findClaimByName('exp')->getValue(), 3); | ||||
|         /** @noinspection NullPointerExceptionInspection */ | ||||
|         $this->assertSame('ely|1', $payloads->findClaimByName('sub')->getValue()); | ||||
|         /** @noinspection NullPointerExceptionInspection */ | ||||
|         $this->assertSame('accounts_web_user', $payloads->findClaimByName('ely-scopes')->getValue()); | ||||
|         /** @noinspection NullPointerExceptionInspection */ | ||||
|         $this->assertSame($session->id, $payloads->findClaimByName('jti')->getValue(), 'session has not changed'); | ||||
|     } | ||||
|  | ||||
|     public function testGetActiveSession() { | ||||
|         /** @var Account $account */ | ||||
|         $account = $this->tester->grabFixture('accounts', 'admin'); | ||||
|         /** @var AccountSession $session */ | ||||
|         $session = $this->tester->grabFixture('sessions', 'admin'); | ||||
|         $token = $this->component->createJwtAuthenticationToken($account, $session); | ||||
|         $jwt = $this->component->serializeToken($token); | ||||
|         // User is guest | ||||
|         $component = new Component(); | ||||
|         $this->assertNull($component->getActiveSession()); | ||||
|  | ||||
|         /** @var Component|\PHPUnit\Framework\MockObject\MockObject $component */ | ||||
|         $component = $this->getMockBuilder(Component::class) | ||||
|             ->setMethods(['getIsGuest']) | ||||
|             ->getMock(); | ||||
|         // Identity is a Oauth2Identity | ||||
|         $component->setIdentity(mock(Oauth2Identity::class)); | ||||
|         $this->assertNull($component->getActiveSession()); | ||||
|  | ||||
|         $component | ||||
|             ->method('getIsGuest') | ||||
|             ->willReturn(false); | ||||
|         // Identity is correct, but have no jti claim | ||||
|         /** @var JwtIdentity|\Mockery\MockInterface $identity */ | ||||
|         $identity = mock(JwtIdentity::class); | ||||
|         $identity->shouldReceive('getToken')->andReturn(new Token()); | ||||
|         $component->setIdentity($identity); | ||||
|         $this->assertNull($component->getActiveSession()); | ||||
|  | ||||
|         $this->mockAuthorizationHeader($jwt); | ||||
|         // Identity is correct and has jti claim, but there is no associated session | ||||
|         /** @var JwtIdentity|\Mockery\MockInterface $identity */ | ||||
|         $identity = mock(JwtIdentity::class); | ||||
|         $identity->shouldReceive('getToken')->andReturn(new Token([], ['jti' => new Basic('jti', 999999)])); | ||||
|         $component->setIdentity($identity); | ||||
|         $this->assertNull($component->getActiveSession()); | ||||
|  | ||||
|         $foundSession = $component->getActiveSession(); | ||||
|         $this->assertInstanceOf(AccountSession::class, $foundSession); | ||||
|         $this->assertSame($session->id, $foundSession->id); | ||||
|         // Identity is correct, has jti claim and associated session exists | ||||
|         /** @var JwtIdentity|\Mockery\MockInterface $identity */ | ||||
|         $identity = mock(JwtIdentity::class); | ||||
|         $identity->shouldReceive('getToken')->andReturn(new Token([], ['jti' => new Basic('jti', 1)])); | ||||
|         $component->setIdentity($identity); | ||||
|         $session = $component->getActiveSession(); | ||||
|         $this->assertNotNull($session); | ||||
|         $this->assertSame(1, $session->id); | ||||
|     } | ||||
|  | ||||
|     public function testTerminateSessions() { | ||||
| @@ -95,7 +78,6 @@ class ComponentTest extends TestCase { | ||||
|  | ||||
|         /** @var Account $account */ | ||||
|         $account = $this->tester->grabFixture('accounts', 'admin'); | ||||
|         $component->createJwtAuthenticationToken($account); | ||||
|  | ||||
|         // Dry run: no sessions should be removed | ||||
|         $component->terminateSessions($account, Component::KEEP_MINECRAFT_SESSIONS | Component::KEEP_SITE_SESSIONS); | ||||
| @@ -119,24 +101,4 @@ class ComponentTest extends TestCase { | ||||
|         $this->assertEmpty($account->getMinecraftAccessKeys()->all()); | ||||
|     } | ||||
|  | ||||
|     private function mockRequest($userIP = '127.0.0.1') { | ||||
|         /** @var Request|\Mockery\MockInterface $request */ | ||||
|         $request = mock(Request::class . '[getHostInfo,getUserIP]')->makePartial(); | ||||
|         $request->shouldReceive('getHostInfo')->andReturn('http://localhost'); | ||||
|         $request->shouldReceive('getUserIP')->andReturn($userIP); | ||||
|  | ||||
|         Yii::$app->set('request', $request); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $bearerToken | ||||
|      */ | ||||
|     private function mockAuthorizationHeader($bearerToken = null) { | ||||
|         if ($bearerToken !== null) { | ||||
|             $bearerToken = 'Bearer ' . $bearerToken; | ||||
|         } | ||||
|  | ||||
|         Yii::$app->request->headers->set('Authorization', $bearerToken); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -5,9 +5,9 @@ namespace codeception\api\unit\components\User; | ||||
|  | ||||
| use api\components\User\JwtIdentity; | ||||
| use api\tests\unit\TestCase; | ||||
| use Carbon\Carbon; | ||||
| use common\tests\fixtures\AccountFixture; | ||||
| use Emarref\Jwt\Claim\Expiration as ExpirationClaim; | ||||
| use Yii; | ||||
| use yii\web\UnauthorizedHttpException; | ||||
|  | ||||
| class JwtIdentityTest extends TestCase { | ||||
|  | ||||
| @@ -18,40 +18,77 @@ class JwtIdentityTest extends TestCase { | ||||
|     } | ||||
|  | ||||
|     public function testFindIdentityByAccessToken() { | ||||
|         $token = $this->generateToken(); | ||||
|         $token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlbHktc2NvcGVzIjoiYWNjb3VudHNfd2ViX3VzZXIiLCJpYXQiOjE1NjQ2MTA1NDIsImV4cCI6MTU2NDYxNDE0Miwic3ViIjoiZWx5fDEifQ.4Oidvuo4spvUf9hkpHR72eeqZUh2Zbxh_L8Od3vcgTj--0iOrcOEp6zwmEW6vF7BTHtjz2b3mXce61bqsCjXjQ'; | ||||
|         /** @var JwtIdentity $identity */ | ||||
|         $identity = JwtIdentity::findIdentityByAccessToken($token); | ||||
|         $this->assertSame($token, $identity->getId()); | ||||
|         $this->assertSame($this->tester->grabFixture('accounts', 'admin')['id'], $identity->getAccount()->id); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @expectedException \yii\web\UnauthorizedHttpException | ||||
|      * @expectedExceptionMessage Token expired | ||||
|      */ | ||||
|     public function testFindIdentityByAccessTokenWithExpiredToken() { | ||||
|         $expiredToken = $this->generateToken(time() - 3600); | ||||
|         JwtIdentity::findIdentityByAccessToken($expiredToken); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @expectedException \yii\web\UnauthorizedHttpException | ||||
|      * @expectedExceptionMessage Incorrect token | ||||
|      */ | ||||
|     public function testFindIdentityByAccessTokenWithEmptyToken() { | ||||
|         JwtIdentity::findIdentityByAccessToken(''); | ||||
|     } | ||||
|  | ||||
|     private function generateToken(int $expiresAt = null): string { | ||||
|         /** @var \api\components\User\Component $component */ | ||||
|         $component = Yii::$app->user; | ||||
|         $this->assertSame($token, (string)$identity->getToken()); | ||||
|         /** @var \common\models\Account $account */ | ||||
|         $account = $this->tester->grabFixture('accounts', 'admin'); | ||||
|         $token = $component->createJwtAuthenticationToken($account); | ||||
|         if ($expiresAt !== null) { | ||||
|             $token->addClaim(new ExpirationClaim($expiresAt)); | ||||
|         $this->assertSame($account->id, $identity->getAccount()->id); | ||||
|     } | ||||
|  | ||||
|         return $component->serializeToken($token); | ||||
|     /** | ||||
|      * @dataProvider getFindIdentityByAccessTokenInvalidCases | ||||
|      */ | ||||
|     public function testFindIdentityByAccessTokenInvalidCases(string $token, string $expectedExceptionMessage) { | ||||
|         $this->expectException(UnauthorizedHttpException::class); | ||||
|         $this->expectExceptionMessage($expectedExceptionMessage); | ||||
|         JwtIdentity::findIdentityByAccessToken($token); | ||||
|     } | ||||
|  | ||||
|     public function getFindIdentityByAccessTokenInvalidCases() { | ||||
|         yield 'expired token' => [ | ||||
|             'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlbHktc2NvcGVzIjoiYWNjb3VudHNfd2ViX3VzZXIiLCJpYXQiOjE1NjQ2MDMzNDIsImV4cCI6MTU2NDYwNjk0Miwic3ViIjoiZWx5fDEifQ.36cDWyiXRArv-lgK_S5dyC5m_Ddytwkb78tMrxcPcbWEpoeg2VtwPC7zr6NI0cd0CuLw6InC2hZ9Ey95SSOsHw', | ||||
|             'Token expired', | ||||
|         ]; | ||||
|         yield 'iat from future' => [ | ||||
|             'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlbHktc2NvcGVzIjoiYWNjb3VudHNfd2ViX3VzZXIiLCJpYXQiOjE1NjQ2MTc3NDIsImV4cCI6MTU2NDYxNDE0Miwic3ViIjoiZWx5fDEifQ._6hj6XUSmSLibgT9ZE1Pokf4oI9r-d6tEc1z2J-fBlr1710Qiso5yNcXqb3Z_xy7Qtemyq8jOlOZA8DvmkVBrg', | ||||
|             'Incorrect token', | ||||
|         ]; | ||||
|         yield 'invalid signature' => [ | ||||
|             'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlbHktc2NvcGVzIjoiYWNjb3VudHNfd2ViX3VzZXIiLCJpYXQiOjE1NjQ2MTA1NDIsImV4cCI6MTU2NDYxNDE0Miwic3ViIjoiZWx5fDEifQ.yth31f2PyhUkYSfBlizzUXWIgOvxxk8gNP-js0z8g1OT5rig40FPTIkgsZRctAwAAlj6QoIWW7-hxLTcSb2vmw', | ||||
|             'Incorrect token', | ||||
|         ]; | ||||
|         yield 'invalid sub' => [ | ||||
|             'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlbHktc2NvcGVzIjoiYWNjb3VudHNfd2ViX3VzZXIiLCJpYXQiOjE1NjQ2MTA1NDIsImV4cCI6MTU2NDYxNDE0Miwic3ViIjoxMjM0fQ.yigP5nWFdX0ktbuZC_Unb9bWxpAVd7Nv8Fb1Vsa0t5WkVA88VbhPi2P-CenbDOy8ngwoGV9m3c3upMs2V3gqvw', | ||||
|             'Incorrect token', | ||||
|         ]; | ||||
|         yield 'empty token' => ['', 'Incorrect token']; | ||||
|     } | ||||
|  | ||||
|     public function testGetAccount() { | ||||
|         // Token with sub claim | ||||
|         $identity = JwtIdentity::findIdentityByAccessToken('eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlbHktc2NvcGVzIjoiYWNjb3VudHNfd2ViX3VzZXIiLCJpYXQiOjE1NjQ2MTA1NDIsImV4cCI6MTU2NDYxNDE0Miwic3ViIjoiZWx5fDEifQ.4Oidvuo4spvUf9hkpHR72eeqZUh2Zbxh_L8Od3vcgTj--0iOrcOEp6zwmEW6vF7BTHtjz2b3mXce61bqsCjXjQ'); | ||||
|         $this->assertSame(1, $identity->getAccount()->id); | ||||
|  | ||||
|         // Sub presented, but account not exists | ||||
|         $identity = JwtIdentity::findIdentityByAccessToken('eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlbHktc2NvcGVzIjoiYWNjb3VudHNfd2ViX3VzZXIiLCJpYXQiOjE1NjQ2MTA1NDIsImV4cCI6MTU2NDYxNDE0Miwic3ViIjoiZWx5fDk5OTk5In0.1pAnhkR-_ZqzjLBR-PNIMJUXRSUK3aYixrFNKZg2ynPNPiDvzh8U-iBTT6XRfMP5nvfXZucRpoPVoiXtx40CUQ'); | ||||
|         $this->assertNull($identity->getAccount()); | ||||
|  | ||||
|         // Token without sub claim | ||||
|         $identity = JwtIdentity::findIdentityByAccessToken('eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlbHktc2NvcGVzIjoiYWNjb3VudHNfd2ViX3VzZXIiLCJpYXQiOjE1NjQ2MTA1NDIsImV4cCI6MTU2NDYxNDE0Mn0.QxmYgSflZOQmhzYRr8bowU767yu4yKgTVaho0MPuyCmUfZO_0O0SQASMKVILf-wlT0ODTTG7vD753a2MTAmPmw'); | ||||
|         $this->assertNull($identity->getAccount()); | ||||
|     } | ||||
|  | ||||
|     public function testGetAssignedPermissions() { | ||||
|         // Token with ely-scopes claim | ||||
|         $identity = JwtIdentity::findIdentityByAccessToken('eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlbHktc2NvcGVzIjoicGVybTEscGVybTIscGVybTMiLCJpYXQiOjE1NjQ2MTA1NDIsImV4cCI6MTU2NDYxNDE0Miwic3ViIjoiZWx5fDEifQ.MO6T92EOFcZSPIdK8VBUG0qyV-pdayzOPQmpWLPwpl1933E9ann9GdV49piX1IfLHeCHVGThm5_v7AJgyZ5Oaw'); | ||||
|         $this->assertSame(['perm1', 'perm2', 'perm3'], $identity->getAssignedPermissions()); | ||||
|  | ||||
|         // Token without sub claim | ||||
|         $identity = JwtIdentity::findIdentityByAccessToken('eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJpYXQiOjE1NjQ2MTA1NDIsImV4cCI6MTU2NDYxNDE0Miwic3ViIjoiZWx5fDEifQ.jsjv2dDetSxu4xivlHoTeDUhqsl-cxSI6SktufJhwR9wqDgQCVIONiqQCUzTzyTwyAz4Ztvel4lKjMCstdJOEw'); | ||||
|         $this->assertSame([], $identity->getAssignedPermissions()); | ||||
|     } | ||||
|  | ||||
|     protected function _before() { | ||||
|         parent::_before(); | ||||
|         Carbon::setTestNow(Carbon::create(2019, 8, 1, 1, 2, 22, 'Europe/Minsk')); | ||||
|     } | ||||
|  | ||||
|     protected function _after() { | ||||
|         parent::_after(); | ||||
|         Carbon::setTestNow(); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace api\tests\_support\models\authentication; | ||||
|  | ||||
| use api\components\User\Component; | ||||
| use api\components\User\IdentityFactory; | ||||
| use api\models\authentication\LogoutForm; | ||||
| use api\tests\unit\TestCase; | ||||
| use Codeception\Specify; | ||||
| @@ -16,7 +17,6 @@ class LogoutFormTest extends TestCase { | ||||
|         $this->specify('No actions if active session is not exists', function() { | ||||
|             $userComp = $this | ||||
|                 ->getMockBuilder(Component::class) | ||||
|                 ->setConstructorArgs([$this->getComponentArgs()]) | ||||
|                 ->setMethods(['getActiveSession']) | ||||
|                 ->getMock(); | ||||
|             $userComp | ||||
| @@ -42,7 +42,6 @@ class LogoutFormTest extends TestCase { | ||||
|  | ||||
|             $userComp = $this | ||||
|                 ->getMockBuilder(Component::class) | ||||
|                 ->setConstructorArgs([$this->getComponentArgs()]) | ||||
|                 ->setMethods(['getActiveSession']) | ||||
|                 ->getMock(); | ||||
|             $userComp | ||||
| @@ -57,15 +56,4 @@ class LogoutFormTest extends TestCase { | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private function getComponentArgs() { | ||||
|         return [ | ||||
|             'identityClass' => IdentityFactory::class, | ||||
|             'enableSession' => false, | ||||
|             'loginUrl' => null, | ||||
|             'secret' => 'secret', | ||||
|             'publicKeyPath' => 'data/certs/public.crt', | ||||
|             'privateKeyPath' => 'data/certs/private.key', | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,8 @@ use api\tests\unit\TestCase; | ||||
| use Codeception\Specify; | ||||
| use common\models\AccountSession; | ||||
| use common\tests\fixtures\AccountSessionFixture; | ||||
| use Yii; | ||||
| use yii\web\Request; | ||||
|  | ||||
| class RefreshTokenFormTest extends TestCase { | ||||
|     use Specify; | ||||
| @@ -18,34 +20,36 @@ class RefreshTokenFormTest extends TestCase { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function testValidateRefreshToken() { | ||||
|         $this->specify('error.refresh_token_not_exist if passed token not exists', function() { | ||||
|             /** @var RefreshTokenForm $model */ | ||||
|             $model = new class extends RefreshTokenForm { | ||||
|                 public function getSession() { | ||||
|                     return null; | ||||
|                 } | ||||
|             }; | ||||
|             $model->validateRefreshToken(); | ||||
|             $this->assertSame(['error.refresh_token_not_exist'], $model->getErrors('refresh_token')); | ||||
|         }); | ||||
|  | ||||
|         $this->specify('no errors if token exists', function() { | ||||
|             /** @var RefreshTokenForm $model */ | ||||
|             $model = new class extends RefreshTokenForm { | ||||
|                 public function getSession() { | ||||
|                     return new AccountSession(); | ||||
|                 } | ||||
|             }; | ||||
|             $model->validateRefreshToken(); | ||||
|             $this->assertEmpty($model->getErrors('refresh_token')); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public function testRenew() { | ||||
|         /** @var Request|\Mockery\MockInterface $request */ | ||||
|         $request = mock(Request::class . '[getUserIP]')->makePartial(); | ||||
|         $request->shouldReceive('getUserIP')->andReturn('10.1.2.3'); | ||||
|         Yii::$app->set('request', $request); | ||||
|  | ||||
|         $model = new RefreshTokenForm(); | ||||
|         $model->refresh_token = $this->tester->grabFixture('sessions', 'admin')['refresh_token']; | ||||
|         $this->assertNotNull($model->renew()); | ||||
|         $model->refresh_token = 'SOutIr6Seeaii3uqMVy3Wan8sKFVFrNz'; | ||||
|         $result = $model->renew(); | ||||
|         $this->assertNotNull($result); | ||||
|         $this->assertSame('SOutIr6Seeaii3uqMVy3Wan8sKFVFrNz', $result->getRefreshToken()); | ||||
|  | ||||
|         $token = $result->getToken(); | ||||
|         $this->assertSame('ely|1', $token->getClaim('sub')); | ||||
|         $this->assertSame('accounts_web_user', $token->getClaim('ely-scopes')); | ||||
|         $this->assertEqualsWithDelta(time(), $token->getClaim('iat'), 5); | ||||
|         $this->assertEqualsWithDelta(time() + 3600, $token->getClaim('exp'), 5); | ||||
|         $this->assertSame(1, $token->getClaim('jti')); | ||||
|  | ||||
|         /** @var AccountSession $session */ | ||||
|         $session = AccountSession::findOne(['refresh_token' => 'SOutIr6Seeaii3uqMVy3Wan8sKFVFrNz']); | ||||
|         $this->assertEqualsWithDelta(time(), $session->last_refreshed_at, 5); | ||||
|         $this->assertSame('10.1.2.3', $session->getReadableIp()); | ||||
|     } | ||||
|  | ||||
|     public function testRenewWithInvalidRefreshToken() { | ||||
|         $model = new RefreshTokenForm(); | ||||
|         $model->refresh_token = 'unknown refresh token'; | ||||
|         $this->assertNull($model->renew()); | ||||
|         $this->assertSame(['error.refresh_token_not_exist'], $model->getErrors('refresh_token')); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace api\tests\unit\modules\accounts\models; | ||||
|  | ||||
| use api\components\User\Component; | ||||
| use api\components\User\IdentityFactory; | ||||
| use api\modules\accounts\models\ChangePasswordForm; | ||||
| use api\tests\unit\TestCase; | ||||
| use common\components\UserPass; | ||||
| @@ -56,14 +57,7 @@ class ChangePasswordFormTest extends TestCase { | ||||
|     } | ||||
|  | ||||
|     public function testPerformAction() { | ||||
|         $component = mock(Component::class . '[terminateSessions]', [[ | ||||
|             'identityClass' => IdentityFactory::class, | ||||
|             'enableSession' => false, | ||||
|             'loginUrl' => null, | ||||
|             'secret' => 'secret', | ||||
|             'publicKeyPath' => 'data/certs/public.crt', | ||||
|             'privateKeyPath' => 'data/certs/private.key', | ||||
|         ]]); | ||||
|         $component = mock(Component::class . '[terminateSessions]'); | ||||
|         $component->shouldNotReceive('terminateSessions'); | ||||
|  | ||||
|         Yii::$app->set('user', $component); | ||||
| @@ -118,14 +112,7 @@ class ChangePasswordFormTest extends TestCase { | ||||
|         $account->setPassword('password_0'); | ||||
|  | ||||
|         /** @var Component|\Mockery\MockInterface $component */ | ||||
|         $component = mock(Component::class . '[terminateSessions]', [[ | ||||
|             'identityClass' => IdentityFactory::class, | ||||
|             'enableSession' => false, | ||||
|             'loginUrl' => null, | ||||
|             'secret' => 'secret', | ||||
|             'publicKeyPath' => 'data/certs/public.crt', | ||||
|             'privateKeyPath' => 'data/certs/private.key', | ||||
|         ]]); | ||||
|         $component = mock(Component::class . '[terminateSessions]'); | ||||
|         $component->shouldReceive('terminateSessions')->once()->withArgs([$account, Component::KEEP_CURRENT_SESSION]); | ||||
|  | ||||
|         Yii::$app->set('user', $component); | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace api\tests\unit\modules\accounts\models; | ||||
|  | ||||
| use api\components\User\Component; | ||||
| use api\components\User\IdentityFactory; | ||||
| use api\modules\accounts\models\EnableTwoFactorAuthForm; | ||||
| use api\tests\unit\TestCase; | ||||
| use common\helpers\Error as E; | ||||
| @@ -19,14 +20,7 @@ class EnableTwoFactorAuthFormTest extends TestCase { | ||||
|         $account->otp_secret = 'mock secret'; | ||||
|  | ||||
|         /** @var Component|\Mockery\MockInterface $component */ | ||||
|         $component = mock(Component::class . '[terminateSessions]', [[ | ||||
|             'identityClass' => IdentityFactory::class, | ||||
|             'enableSession' => false, | ||||
|             'loginUrl' => null, | ||||
|             'secret' => 'secret', | ||||
|             'publicKeyPath' => 'data/certs/public.crt', | ||||
|             'privateKeyPath' => 'data/certs/private.key', | ||||
|         ]]); | ||||
|         $component = mock(Component::class . '[terminateSessions]'); | ||||
|         $component->shouldReceive('terminateSessions')->withArgs([$account, Component::KEEP_CURRENT_SESSION]); | ||||
|  | ||||
|         Yii::$app->set('user', $component); | ||||
|   | ||||
| @@ -1,12 +1,11 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace common\tests\unit\rbac\rules; | ||||
| namespace api\tests\unit\rbac\rules; | ||||
| 
 | ||||
| use api\components\User\Component; | ||||
| use api\components\User\IdentityInterface; | ||||
| use api\rbac\rules\AccountOwner; | ||||
| use common\models\Account; | ||||
| use common\rbac\rules\AccountOwner; | ||||
| use common\tests\unit\TestCase; | ||||
| use Yii; | ||||
| use yii\rbac\Item; | ||||
| @@ -14,35 +13,33 @@ use const common\LATEST_RULES_VERSION; | ||||
| 
 | ||||
| class AccountOwnerTest extends TestCase { | ||||
| 
 | ||||
|     public function testIdentityIsNull() { | ||||
|         $component = mock(Component::class . '[findIdentityByAccessToken]'); | ||||
|         $component->makePartial(); | ||||
|         $component->shouldReceive('findIdentityByAccessToken')->andReturn(null); | ||||
| 
 | ||||
|         Yii::$app->set('user', $component); | ||||
| 
 | ||||
|         $this->assertFalse((new AccountOwner())->execute('some token', new Item(), ['accountId' => 123])); | ||||
|     } | ||||
| 
 | ||||
|     public function testExecute() { | ||||
|         $rule = new AccountOwner(); | ||||
|         $item = new Item(); | ||||
| 
 | ||||
|         // Identity is null
 | ||||
|         $this->assertFalse($rule->execute('some token', $item, ['accountId' => 123])); | ||||
| 
 | ||||
|         // Identity presented, but have no account
 | ||||
|         /** @var IdentityInterface|\Mockery\MockInterface $identity */ | ||||
|         $identity = mock(IdentityInterface::class); | ||||
|         $identity->shouldReceive('getAccount')->andReturn(null); | ||||
|         Yii::$app->user->setIdentity($identity); | ||||
| 
 | ||||
|         $this->assertFalse($rule->execute('some token', $item, ['accountId' => 123])); | ||||
| 
 | ||||
|         // Identity has an account
 | ||||
|         $account = new Account(); | ||||
|         $account->id = 1; | ||||
|         $account->status = Account::STATUS_ACTIVE; | ||||
|         $account->rules_agreement_version = LATEST_RULES_VERSION; | ||||
| 
 | ||||
|         /** @var IdentityInterface|\Mockery\MockInterface $identity */ | ||||
|         $identity = mock(IdentityInterface::class); | ||||
|         $identity->shouldReceive('getAccount')->andReturn($account); | ||||
| 
 | ||||
|         $component = mock(Component::class . '[findIdentityByAccessToken]'); | ||||
|         $component->makePartial(); | ||||
|         $component->shouldReceive('findIdentityByAccessToken')->withArgs(['token'])->andReturn($identity); | ||||
|         Yii::$app->user->setIdentity($identity); | ||||
| 
 | ||||
|         Yii::$app->set('user', $component); | ||||
| 
 | ||||
|         $this->assertFalse($rule->execute('token', $item, [])); | ||||
|         $this->assertFalse($rule->execute('token', $item, ['accountId' => 2])); | ||||
|         $this->assertFalse($rule->execute('token', $item, ['accountId' => '2'])); | ||||
|         $this->assertTrue($rule->execute('token', $item, ['accountId' => 1])); | ||||
| @@ -56,4 +53,12 @@ class AccountOwnerTest extends TestCase { | ||||
|         $this->assertFalse($rule->execute('token', $item, ['accountId' => 1, 'optionalRules' => true])); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @expectedException \InvalidArgumentException | ||||
|      */ | ||||
|     public function testExecuteWithoutAccountId() { | ||||
|         $rule = new AccountOwner(); | ||||
|         $this->assertFalse($rule->execute('token', new Item(), [])); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @@ -1,13 +1,12 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace common\tests\unit\rbac\rules; | ||||
| namespace api\tests\unit\rbac\rules; | ||||
| 
 | ||||
| use api\components\User\Component; | ||||
| use api\components\User\IdentityInterface; | ||||
| use api\rbac\Permissions as P; | ||||
| use api\rbac\rules\OauthClientOwner; | ||||
| use common\models\Account; | ||||
| use common\rbac\Permissions as P; | ||||
| use common\rbac\rules\OauthClientOwner; | ||||
| use common\tests\fixtures\OauthClientFixture; | ||||
| use common\tests\unit\TestCase; | ||||
| use Yii; | ||||
| @@ -26,6 +25,21 @@ class OauthClientOwnerTest extends TestCase { | ||||
|         $rule = new OauthClientOwner(); | ||||
|         $item = new Item(); | ||||
| 
 | ||||
|         // Client not exists (we expect true to let controller throw corresponding 404 exception)
 | ||||
|         $this->assertTrue($rule->execute('some token', $item, ['clientId' => 'not exists client id'])); | ||||
| 
 | ||||
|         // Client exists, but identity is null
 | ||||
|         $this->assertFalse($rule->execute('some token', $item, ['clientId' => 'ely'])); | ||||
| 
 | ||||
|         // Client exists, identity presented, but have no account
 | ||||
|         /** @var IdentityInterface|\Mockery\MockInterface $identity */ | ||||
|         $identity = mock(IdentityInterface::class); | ||||
|         $identity->shouldReceive('getAccount')->andReturn(null); | ||||
|         Yii::$app->user->setIdentity($identity); | ||||
| 
 | ||||
|         $this->assertFalse($rule->execute('some token', $item, ['clientId' => 'ely'])); | ||||
| 
 | ||||
|         // Identity has an account
 | ||||
|         $account = new Account(); | ||||
|         $account->id = 1; | ||||
|         $account->status = Account::STATUS_ACTIVE; | ||||
| @@ -34,15 +48,8 @@ class OauthClientOwnerTest extends TestCase { | ||||
|         /** @var IdentityInterface|\Mockery\MockInterface $identity */ | ||||
|         $identity = mock(IdentityInterface::class); | ||||
|         $identity->shouldReceive('getAccount')->andReturn($account); | ||||
|         Yii::$app->user->setIdentity($identity); | ||||
| 
 | ||||
|         /** @var Component|\Mockery\MockInterface $component */ | ||||
|         $component = mock(Component::class . '[findIdentityByAccessToken]'); | ||||
|         $component->makePartial(); | ||||
|         $component->shouldReceive('findIdentityByAccessToken')->withArgs(['token'])->andReturn($identity); | ||||
| 
 | ||||
|         Yii::$app->set('user', $component); | ||||
| 
 | ||||
|         $this->assertFalse($rule->execute('token', $item, [])); | ||||
|         $this->assertTrue($rule->execute('token', $item, ['clientId' => 'admin-oauth-client'])); | ||||
|         $this->assertTrue($rule->execute('token', $item, ['clientId' => 'not-exists-client'])); | ||||
|         $account->id = 2; | ||||
| @@ -52,4 +59,12 @@ class OauthClientOwnerTest extends TestCase { | ||||
|         $this->assertFalse($rule->execute('token', $item, ['accountId' => 1])); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @expectedException \InvalidArgumentException | ||||
|      */ | ||||
|     public function testExecuteWithoutClientId() { | ||||
|         $rule = new OauthClientOwner(); | ||||
|         $this->assertFalse($rule->execute('token', new Item(), [])); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @@ -1,11 +1,11 @@ | ||||
| <?php | ||||
| namespace codeception\api\unit\validators; | ||||
|  | ||||
| use api\rbac\Permissions as P; | ||||
| use api\tests\unit\TestCase; | ||||
| use api\validators\PasswordRequiredValidator; | ||||
| use common\helpers\Error as E; | ||||
| use common\models\Account; | ||||
| use common\rbac\Permissions as P; | ||||
| use common\tests\_support\ProtectedCaller; | ||||
| use yii\web\User; | ||||
|  | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| <?php | ||||
| namespace api\validators; | ||||
|  | ||||
| use api\rbac\Permissions as P; | ||||
| use common\helpers\Error as E; | ||||
| use common\models\Account; | ||||
| use common\rbac\Permissions as P; | ||||
| use yii\base\InvalidConfigException; | ||||
| use yii\di\Instance; | ||||
| use yii\validators\Validator; | ||||
|   | ||||
| @@ -17,5 +17,6 @@ coverage: | ||||
|     exclude: | ||||
|       - config/* | ||||
|       - mail/* | ||||
|       - tests/* | ||||
|       - codeception.dist.yml | ||||
|       - codeception.yml | ||||
|   | ||||
| @@ -98,7 +98,7 @@ return [ | ||||
|             'class' => api\components\OAuth2\Component::class, | ||||
|         ], | ||||
|         'authManager' => [ | ||||
|             'class' => common\rbac\Manager::class, | ||||
|             'class' => \api\rbac\Manager::class, | ||||
|             'itemFile' => '@common/rbac/.generated/items.php', | ||||
|             'ruleFile' => '@common/rbac/.generated/rules.php', | ||||
|         ], | ||||
|   | ||||
| @@ -1,34 +0,0 @@ | ||||
| <?php | ||||
| namespace common\rbac; | ||||
|  | ||||
| use Yii; | ||||
| use yii\rbac\PhpManager; | ||||
|  | ||||
| class Manager extends PhpManager { | ||||
|  | ||||
|     /** | ||||
|      * In our application the permissions are given not to users but to tokens, | ||||
|      * so we receive $accessToken here and extract all the assigned scopes from it. | ||||
|      * | ||||
|      * In Yii2, the mechanism of recursive permissions checking requires that the array with permissions | ||||
|      * is indexed by the keys of these rights, so at the end we turn the array inside out. | ||||
|      * | ||||
|      * @param string $accessToken | ||||
|      * @return string[] | ||||
|      */ | ||||
|     public function getAssignments($accessToken): array { | ||||
|         $identity = Yii::$app->user->findIdentityByAccessToken($accessToken); | ||||
|         if ($identity === null) { | ||||
|             return []; | ||||
|         } | ||||
|  | ||||
|         /** @noinspection NullPointerExceptionInspection */ | ||||
|         $permissions = $identity->getAssignedPermissions(); | ||||
|         if (empty($permissions)) { | ||||
|             return []; | ||||
|         } | ||||
|  | ||||
|         return array_flip($permissions); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -16,12 +16,12 @@ | ||||
|         "domnikl/statsd": "^2.6", | ||||
|         "ely/mojang-api": "^0.2.0", | ||||
|         "ely/yii2-tempmail-validator": "^2.0", | ||||
|         "emarref/jwt": "~1.0.3", | ||||
|         "goaop/framework": "^2.2.0", | ||||
|         "guzzlehttp/guzzle": "^6.0.0", | ||||
|         "lcobucci/jwt": "^3.3", | ||||
|         "league/oauth2-server": "^4.1", | ||||
|         "mito/yii2-sentry": "^1.0", | ||||
|         "nesbot/carbon": "^2.22", | ||||
|         "paragonie/constant_time_encoding": "^2.0", | ||||
|         "ramsey/uuid": "^3.5", | ||||
|         "spomky-labs/otphp": "^9.0.2", | ||||
|   | ||||
							
								
								
									
										244
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										244
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							| @@ -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": "2dfa204a51a82cd7c7d6a5b7d1ccbc0c", | ||||
|     "content-hash": "2c49fce9e25e3bc27dc3ae43ac0c079b", | ||||
|     "packages": [ | ||||
|         { | ||||
|             "name": "bacon/bacon-qr-code", | ||||
| @@ -701,48 +701,6 @@ | ||||
|             ], | ||||
|             "time": "2017-09-30T22:51:45+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "emarref/jwt", | ||||
|             "version": "1.0.3", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/emarref/jwt.git", | ||||
|                 "reference": "79f563750ff90dabd4fa677c4b4e5ec9ed52d9b4" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/emarref/jwt/zipball/79f563750ff90dabd4fa677c4b4e5ec9ed52d9b4", | ||||
|                 "reference": "79f563750ff90dabd4fa677c4b4e5ec9ed52d9b4", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
|                 "php": ">=5.4" | ||||
|             }, | ||||
|             "require-dev": { | ||||
|                 "phpunit/phpunit": "*" | ||||
|             }, | ||||
|             "suggest": { | ||||
|                 "ext-openssl": "Enables more token encryption options" | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "autoload": { | ||||
|                 "psr-4": { | ||||
|                     "Emarref\\Jwt\\": "src/" | ||||
|                 } | ||||
|             }, | ||||
|             "notification-url": "https://packagist.org/downloads/", | ||||
|             "license": [ | ||||
|                 "MIT" | ||||
|             ], | ||||
|             "authors": [ | ||||
|                 { | ||||
|                     "name": "Malcolm Fell", | ||||
|                     "email": "emarref@gmail.com" | ||||
|                 } | ||||
|             ], | ||||
|             "description": "A JWT implementation", | ||||
|             "time": "2016-09-05T20:33:06+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "ezyang/htmlpurifier", | ||||
|             "version": "v4.10.0", | ||||
| @@ -1369,6 +1327,73 @@ | ||||
|             ], | ||||
|             "time": "2017-11-28T16:52:35+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "nesbot/carbon", | ||||
|             "version": "2.22.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/briannesbitt/Carbon.git", | ||||
|                 "reference": "1a0e48b5f656065ba3c265b058b25d36c2162a5e" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/1a0e48b5f656065ba3c265b058b25d36c2162a5e", | ||||
|                 "reference": "1a0e48b5f656065ba3c265b058b25d36c2162a5e", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
|                 "ext-json": "*", | ||||
|                 "php": "^7.1.8 || ^8.0", | ||||
|                 "symfony/translation": "^3.4 || ^4.0" | ||||
|             }, | ||||
|             "require-dev": { | ||||
|                 "friendsofphp/php-cs-fixer": "^2.14 || ^3.0", | ||||
|                 "kylekatarnls/multi-tester": "^1.1", | ||||
|                 "phpmd/phpmd": "dev-php-7.1-compatibility", | ||||
|                 "phpstan/phpstan": "^0.11", | ||||
|                 "phpunit/phpunit": "^7.5 || ^8.0", | ||||
|                 "squizlabs/php_codesniffer": "^3.4" | ||||
|             }, | ||||
|             "bin": [ | ||||
|                 "bin/carbon" | ||||
|             ], | ||||
|             "type": "library", | ||||
|             "extra": { | ||||
|                 "laravel": { | ||||
|                     "providers": [ | ||||
|                         "Carbon\\Laravel\\ServiceProvider" | ||||
|                     ] | ||||
|                 } | ||||
|             }, | ||||
|             "autoload": { | ||||
|                 "psr-4": { | ||||
|                     "Carbon\\": "src/Carbon/" | ||||
|                 } | ||||
|             }, | ||||
|             "notification-url": "https://packagist.org/downloads/", | ||||
|             "license": [ | ||||
|                 "MIT" | ||||
|             ], | ||||
|             "authors": [ | ||||
|                 { | ||||
|                     "name": "Brian Nesbitt", | ||||
|                     "email": "brian@nesbot.com", | ||||
|                     "homepage": "http://nesbot.com" | ||||
|                 }, | ||||
|                 { | ||||
|                     "name": "kylekatarnls", | ||||
|                     "homepage": "http://github.com/kylekatarnls" | ||||
|                 } | ||||
|             ], | ||||
|             "description": "A simple API extension for DateTime.", | ||||
|             "homepage": "http://carbon.nesbot.com", | ||||
|             "keywords": [ | ||||
|                 "date", | ||||
|                 "datetime", | ||||
|                 "time" | ||||
|             ], | ||||
|             "time": "2019-07-28T09:02:12+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "nikic/php-parser", | ||||
|             "version": "v4.2.1", | ||||
| @@ -1990,6 +2015,139 @@ | ||||
|             "homepage": "https://symfony.com", | ||||
|             "time": "2019-04-10T16:20:36+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/translation", | ||||
|             "version": "v4.3.3", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/translation.git", | ||||
|                 "reference": "4e3e39cc485304f807622bdc64938e4633396406" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/translation/zipball/4e3e39cc485304f807622bdc64938e4633396406", | ||||
|                 "reference": "4e3e39cc485304f807622bdc64938e4633396406", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
|                 "php": "^7.1.3", | ||||
|                 "symfony/polyfill-mbstring": "~1.0", | ||||
|                 "symfony/translation-contracts": "^1.1.2" | ||||
|             }, | ||||
|             "conflict": { | ||||
|                 "symfony/config": "<3.4", | ||||
|                 "symfony/dependency-injection": "<3.4", | ||||
|                 "symfony/yaml": "<3.4" | ||||
|             }, | ||||
|             "provide": { | ||||
|                 "symfony/translation-implementation": "1.0" | ||||
|             }, | ||||
|             "require-dev": { | ||||
|                 "psr/log": "~1.0", | ||||
|                 "symfony/config": "~3.4|~4.0", | ||||
|                 "symfony/console": "~3.4|~4.0", | ||||
|                 "symfony/dependency-injection": "~3.4|~4.0", | ||||
|                 "symfony/finder": "~2.8|~3.0|~4.0", | ||||
|                 "symfony/http-kernel": "~3.4|~4.0", | ||||
|                 "symfony/intl": "~3.4|~4.0", | ||||
|                 "symfony/service-contracts": "^1.1.2", | ||||
|                 "symfony/var-dumper": "~3.4|~4.0", | ||||
|                 "symfony/yaml": "~3.4|~4.0" | ||||
|             }, | ||||
|             "suggest": { | ||||
|                 "psr/log-implementation": "To use logging capability in translator", | ||||
|                 "symfony/config": "", | ||||
|                 "symfony/yaml": "" | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "extra": { | ||||
|                 "branch-alias": { | ||||
|                     "dev-master": "4.3-dev" | ||||
|                 } | ||||
|             }, | ||||
|             "autoload": { | ||||
|                 "psr-4": { | ||||
|                     "Symfony\\Component\\Translation\\": "" | ||||
|                 }, | ||||
|                 "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 Translation Component", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "time": "2019-07-18T10:34:59+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/translation-contracts", | ||||
|             "version": "v1.1.5", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/translation-contracts.git", | ||||
|                 "reference": "cb4b18ad7b92a26e83b65dde940fab78339e6f3c" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/cb4b18ad7b92a26e83b65dde940fab78339e6f3c", | ||||
|                 "reference": "cb4b18ad7b92a26e83b65dde940fab78339e6f3c", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
|                 "php": "^7.1.3" | ||||
|             }, | ||||
|             "suggest": { | ||||
|                 "symfony/translation-implementation": "" | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "extra": { | ||||
|                 "branch-alias": { | ||||
|                     "dev-master": "1.1-dev" | ||||
|                 } | ||||
|             }, | ||||
|             "autoload": { | ||||
|                 "psr-4": { | ||||
|                     "Symfony\\Contracts\\Translation\\": "" | ||||
|                 } | ||||
|             }, | ||||
|             "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": "Generic abstractions related to translation", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "keywords": [ | ||||
|                 "abstractions", | ||||
|                 "contracts", | ||||
|                 "decoupling", | ||||
|                 "interfaces", | ||||
|                 "interoperability", | ||||
|                 "standards" | ||||
|             ], | ||||
|             "time": "2019-06-13T11:15:36+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "webmozart/assert", | ||||
|             "version": "1.4.0", | ||||
|   | ||||
| @@ -18,6 +18,7 @@ coverage: | ||||
|       - config/* | ||||
|       - runtime/* | ||||
|       - migrations/* | ||||
|       - tests/* | ||||
|       - views/* | ||||
|       - codeception.dist.yml | ||||
|       - codeception.yml | ||||
|   | ||||
| @@ -1,13 +1,14 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace console\controllers; | ||||
|  | ||||
| use common\rbac\Permissions as P; | ||||
| use common\rbac\Roles as R; | ||||
| use common\rbac\rules\AccountOwner; | ||||
| use common\rbac\rules\OauthClientOwner; | ||||
| use InvalidArgumentException; | ||||
| use api\rbac\Permissions as P; | ||||
| use api\rbac\Roles as R; | ||||
| use api\rbac\rules\AccountOwner; | ||||
| use api\rbac\rules\OauthClientOwner; | ||||
| use Webmozart\Assert\Assert; | ||||
| use Yii; | ||||
| use yii\base\ErrorException; | ||||
| use yii\console\Controller; | ||||
| use yii\rbac\ManagerInterface; | ||||
| use yii\rbac\Permission; | ||||
| @@ -84,9 +85,7 @@ class RbacController extends Controller { | ||||
|     private function createRole(string $name): Role { | ||||
|         $authManager = $this->getAuthManager(); | ||||
|         $role = $authManager->createRole($name); | ||||
|         if (!$authManager->add($role)) { | ||||
|             throw new ErrorException('Cannot save role in authManager'); | ||||
|         } | ||||
|         Assert::true($authManager->add($role), 'Cannot save role in authManager'); | ||||
|  | ||||
|         return $role; | ||||
|     } | ||||
| @@ -96,9 +95,7 @@ class RbacController extends Controller { | ||||
|         $permission = $authManager->createPermission($name); | ||||
|         if ($ruleClassName !== null) { | ||||
|             $rule = new $ruleClassName(); | ||||
|             if (!$rule instanceof Rule) { | ||||
|                 throw new InvalidArgumentException('ruleClassName must be rule class name'); | ||||
|             } | ||||
|             Assert::isInstanceOf($rule, Rule::class, 'ruleClassName must be rule class name'); | ||||
|  | ||||
|             $ruleFromAuthManager = $authManager->getRule($rule->name); | ||||
|             if ($ruleFromAuthManager === null) { | ||||
| @@ -108,9 +105,7 @@ class RbacController extends Controller { | ||||
|             $permission->ruleName = $rule->name; | ||||
|         } | ||||
|  | ||||
|         if (!$authManager->add($permission)) { | ||||
|             throw new ErrorException('Cannot save permission in authManager'); | ||||
|         } | ||||
|         Assert::true($authManager->add($permission), 'Cannot save permission in authManager'); | ||||
|  | ||||
|         return $permission; | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user