From f72310c398759841a0f8b52e1aba7990086c0d9c Mon Sep 17 00:00:00 2001 From: Alexey Date: Wed, 15 Jun 2022 11:06:38 +0300 Subject: [PATCH] fix composite auth ignore only/except filters of auth methods (#19418) * fix compositeAuth ignore only/except filters of child auth methods * fix * add tests for wrong token * fix * update changelog * Update CHANGELOG.md Co-authored-by: Bizley --- framework/CHANGELOG.md | 1 + framework/filters/auth/CompositeAuth.php | 8 +- .../filters/auth/CompositeAuthTest.php | 151 ++++++++++++++++-- 3 files changed, 144 insertions(+), 16 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index dc102497c4..e3a9eb3064 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -29,6 +29,7 @@ Yii Framework 2 Change Log - Bug #19368: Fix PHP 8.1 error when `$fileMimeType` is `null` in `yii\validators\FileValidator::validateMimeType()` (bizley) - Enh #19384: Normalize `setBodyParams()` and `getBodyParam()` in `yii\web\Request` (WinterSilence, albertborsos) - Bug #19386: Fix recursive calling `yii\helpers\BaseArrayHelper::htmlDecode()` (WinterSilence) +- Bug #19418: Fix `yii\filters\auth\CompositeAuth` ignoring `only` and `except` options (lesha724) - Enh #19401: Delay `exit(1)` in `yii\base\ErrorHandler::handleFatalError` (arrilot) - Bug #19402: Add shutdown event and fix working directory in `yii\base\ErrorHandler` (WinterSilence) - Enh #19416: Update and improve configurations for `yii\console\controllers\MessageController` (WinterSilence) diff --git a/framework/filters/auth/CompositeAuth.php b/framework/filters/auth/CompositeAuth.php index 37043d4235..4212f5c323 100644 --- a/framework/filters/auth/CompositeAuth.php +++ b/framework/filters/auth/CompositeAuth.php @@ -70,9 +70,11 @@ class CompositeAuth extends AuthMethod } } - $identity = $auth->authenticate($user, $request, $response); - if ($identity !== null) { - return $identity; + if (isset($this->owner->action) && $auth->isActive($this->owner->action)) { + $identity = $auth->authenticate($user, $request, $response); + if ($identity !== null) { + return $identity; + } } } diff --git a/tests/framework/filters/auth/CompositeAuthTest.php b/tests/framework/filters/auth/CompositeAuthTest.php index 7ec57ee123..a9fe7c73e6 100644 --- a/tests/framework/filters/auth/CompositeAuthTest.php +++ b/tests/framework/filters/auth/CompositeAuthTest.php @@ -8,21 +8,40 @@ namespace yiiunit\framework\filters\auth; use Yii; -use yii\filters\auth\AuthMethod; use yii\filters\auth\CompositeAuth; use yii\filters\auth\HttpBearerAuth; +use yii\filters\auth\HttpHeaderAuth; use yii\rest\Controller; -use yii\web\UnauthorizedHttpException; use yiiunit\framework\web\UserIdentity; /** * @author Ezekiel Fernandez */ -class TestAuth extends AuthMethod +class TestAuth extends HttpHeaderAuth { public function authenticate($user, $request, $response) { - return $user; + $authHeader = $request->getHeaders()->get($this->header); + + if ($authHeader !== null) { + if ($this->pattern !== null) { + if (preg_match($this->pattern, $authHeader, $matches)) { + $authHeader = $matches[1]; + } else { + return null; + } + } + + $identity = \yiiunit\framework\filters\stubs\UserIdentity::findIdentity($authHeader); + if ($identity === null) { + $this->challenge($response); + $this->handleFailure($response); + } + + return $identity; + } + + return null; } } @@ -30,6 +49,8 @@ class TestController extends Controller { public $authMethods = []; + public $optional = []; + public function actionA() { return 'success'; @@ -73,6 +94,7 @@ class TestController extends Controller 'authMethods' => $this->authMethods ?: [ TestAuth::className(), ], + 'optional' => $this->optional ], ]; } @@ -107,6 +129,7 @@ class CompositeAuthTest extends \yiiunit\TestCase public function testCallingRunWithCompleteRoute() { /** @var TestController $controller */ + Yii::$app->request->headers->set('X-Api-Key', 'user1'); $controller = Yii::$app->createController('test')[0]; $this->assertEquals('success', $controller->run('test/d')); } @@ -117,6 +140,7 @@ class CompositeAuthTest extends \yiiunit\TestCase public function testRunAction() { /** @var TestController $controller */ + Yii::$app->request->headers->set('X-Api-Key', 'user1'); $controller = Yii::$app->createController('test')[0]; $this->assertEquals('success', $controller->run('b')); } @@ -124,22 +148,123 @@ class CompositeAuthTest extends \yiiunit\TestCase public function testRunButWithActionIdOnly() { /** @var TestController $controller */ + Yii::$app->request->headers->set('X-Api-Key', 'user1'); $controller = Yii::$app->createController('test')[0]; $this->assertEquals('success', $controller->run('c')); } - public function testCompositeAuth() + public function testRunWithWrongToken() { - Yii::$app->request->headers->set('Authorization', base64_encode("foo:bar")); - /** @var TestAuthController $controller */ + /** @var TestController $controller */ + Yii::$app->request->headers->set('X-Api-Key', 'wrong-user'); $controller = Yii::$app->createController('test')[0]; - $controller->authMethods = [ - HttpBearerAuth::className(), - TestAuth::className(), + $this->expectException('yii\web\UnauthorizedHttpException'); + $controller->run('a'); + } + + public function testRunWithoutAuthHeader() + { + /** @var TestController $controller */ + $controller = Yii::$app->createController('test')[0]; + $this->expectException('yii\web\UnauthorizedHttpException'); + $controller->run('a'); + } + + public function testRunWithOptionalAction() + { + /** @var TestController $controller */ + $controller = Yii::$app->createController('test')[0]; + $controller->optional = ['a']; + $this->assertEquals('success', $controller->run('a')); + } + + public function compositeAuthDataProvider() + { + return [ + //base usage + [ + [ + HttpBearerAuth::className(), + TestAuth::className(), + ], + 'b', + true + ], + //empty auth methods + [ + [], + 'b', + true + ], + //only "a", run "b" + [ + [ + HttpBearerAuth::className(), + [ + 'class' => TestAuth::className(), + 'only' => ['a'] + ], + ], + 'b', + false + ], + //only "a", run "a" + [ + [ + HttpBearerAuth::className(), + [ + 'class' => TestAuth::className(), + 'only' => ['a'] + ], + ], + 'a', + true + ], + //except "b", run "a" + [ + [ + HttpBearerAuth::className(), + [ + 'class' => TestAuth::className(), + 'except' => ['b'] + ], + ], + 'a', + true + ], + //except "b", run "b" + [ + [ + HttpBearerAuth::className(), + [ + 'class' => TestAuth::className(), + 'except' => ['b'] + ], + ], + 'b', + false + ] ]; - try { - $this->assertEquals('success', $controller->run('b')); - } catch (UnauthorizedHttpException $e) { + } + + /** + * @param array $authMethods + * @param string $actionId + * @param bool $expectedAuth + * + * @dataProvider compositeAuthDataProvider + */ + public function testCompositeAuth($authMethods, $actionId, $expectedAuth) + { + Yii::$app->request->headers->set('X-Api-Key', 'user1'); + /** @var TestController $controller */ + $controller = Yii::$app->createController('test')[0]; + $controller->authMethods = $authMethods; + if( $expectedAuth) + $this->assertEquals('success', $controller->run($actionId)); + else { + $this->expectException('yii\web\UnauthorizedHttpException'); + $controller->run($actionId); } } }