Skip to content

Commit

Permalink
Merge pull request #64 from chadicus/v3.x
Browse files Browse the repository at this point in the history
Sync v3.x with master
  • Loading branch information
chadicus authored May 9, 2023
2 parents 27a0feb + e6a5818 commit 4a74900
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 80 deletions.
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
"require": {
"php": "^5.6 || ^7.0",
"bshaffer/oauth2-server-php": "^1.9",
"chadicus/slim-oauth2-http": "^3.1",
"chadicus/psr-middleware": "^1.0",
"psr/http-message": "^1.0",
"container-interop/container-interop": "^1.1"
"chadicus/slim-oauth2-http": "^3.1",
"container-interop/container-interop": "^1.1",
"psr/http-message": "^1.0"
},
"require-dev": {
"chadicus/coding-standard": "^1.3",
Expand Down
51 changes: 31 additions & 20 deletions src/Authorization.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@
use Chadicus\Psr\Middleware\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Interop\Container\ContainerInterface;
use OAuth2;

/**
* Slim Middleware to handle OAuth2 Authorization.
*/
class Authorization implements MiddlewareInterface
{
/**
* @var string
*/
const TOKEN_ATTRIBUTE_KEY = 'oauth2-token';

/**
* OAuth2 Server
*
Expand All @@ -32,35 +36,28 @@ class Authorization implements MiddlewareInterface
/**
* Container for token.
*
* @var ArrayAccess|ContainerInterface
* @var mixed
*/
private $container;

/**
* Create a new instance of the Authroization middleware.
*
* @param OAuth2\Server $server The configured OAuth2 server.
* @param ArrayAccess|ContainerInterface $container A container object in which to store the token from the
* request.
* @param array $scopes Scopes required for authorization. $scopes can be given as an
* array of arrays. OR logic will use with each grouping.
* Example:
* Given ['superUser', ['basicUser', 'aPermission']], the request
* will be verified if the request token has 'superUser' scope
* OR 'basicUser' and 'aPermission' as its scope.
* @param OAuth2\Server $server The configured OAuth2 server.
* @param mixed $container A container object in which to store the token from the request.
* @param array $scopes Scopes required for authorization. $scopes can be given as an
* array of arrays. OR logic will use with each grouping.
* Example:
* Given ['superUser', ['basicUser', 'aPermission']], the request
* will be verified if the request token has 'superUser' scope
* OR 'basicUser' and 'aPermission' as its scope.
*
* @throws \InvalidArgumentException Thrown if $container is not an instance of ArrayAccess or ContainerInterface.
*/
public function __construct(OAuth2\Server $server, $container, array $scopes = [])
{
$this->server = $server;
if (!is_a($container, '\\ArrayAccess') && !is_a($container, '\\Interop\\Container\\ContainerInterface')) {
throw new \InvalidArgumentException(
'$container does not implement \\ArrayAccess or \\Interop\\Container\\ContainerInterface'
);
}

$this->container = $container;
$this->container = $this->validateContainer($container);
$this->scopes = $this->formatScopes($scopes);
}

Expand All @@ -78,8 +75,9 @@ public function __invoke(ServerRequestInterface $request, ResponseInterface $res
$oauth2Request = RequestBridge::toOAuth2($request);
foreach ($this->scopes as $scope) {
if ($this->server->verifyResourceRequest($oauth2Request, null, $scope)) {
$this->setToken($this->server->getResourceController()->getToken());
return $next($request, $response);
$token = $this->server->getResourceController()->getToken();
$this->setToken($token);
return $next($request->withAttribute(self::TOKEN_ATTRIBUTE_KEY, $token), $response);
}
}

Expand Down Expand Up @@ -147,4 +145,17 @@ function (&$scope) {

return $scopes;
}

private function validateContainer($container)
{
if (is_a($container, ArrayAccess::class)) {
return $container;
}

if (method_exists($container, 'set')) {
return $container;
}

throw new \InvalidArgumentException("\$container does not implement ArrayAccess or contain a 'set' method");
}
}
130 changes: 73 additions & 57 deletions tests/AuthorizationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,22 @@ public function invoke()

$middleware = new Authorization($server, $container);

$next = function ($request, $response) {
$expectedToken = [
'access_token' => 'atokenvalue',
'client_id' => 'a client id',
'user_id' => 'a user id',
'expires' => 99999999900,
'scope' => null,
];
$test = $this;
$next = function ($request, $response) use ($expectedToken, $test) {
$test->assertSame($expectedToken, $request->getAttribute(Authorization::TOKEN_ATTRIBUTE_KEY));
return $response;
};

$middleware($request, new Response(), $next);

$this->assertSame(
[
'access_token' => 'atokenvalue',
'client_id' => 'a client id',
'user_id' => 'a user id',
'expires' => 99999999900,
'scope' => null,
],
$container['token']
);
$this->assertSame($expectedToken, $container['token']);
}

/**
Expand Down Expand Up @@ -171,23 +171,23 @@ public function withRequiredScope()

$middleware = new Authorization($server, $container);

$next = function ($request, $response) {
$expectedToken = [
'access_token' => 'atokenvalue',
'client_id' => 'a client id',
'user_id' => 'a user id',
'expires' => 99999999900,
'scope' => 'allowFoo anotherScope',
];
$test = $this;
$next = function ($request, $response) use ($expectedToken, $test) {
$test->assertSame($expectedToken, $request->getAttribute(Authorization::TOKEN_ATTRIBUTE_KEY));
return $response;
};

$response = $middleware->withRequiredScope(['allowFoo'])->__invoke($request, new Response(), $next);

$this->assertSame(200, $response->getStatusCode());
$this->assertSame(
[
'access_token' => 'atokenvalue',
'client_id' => 'a client id',
'user_id' => 'a user id',
'expires' => 99999999900,
'scope' => 'allowFoo anotherScope',
],
$container['token']
);
$this->assertSame($expectedToken , $container['token']);
}

/**
Expand Down Expand Up @@ -320,23 +320,22 @@ public function invokeWithEitherScope()

$middleware = new Authorization($server, $container, ['superUser', ['basicUser', 'withPermission']]);

$next = function ($request, $response) {
$expectedToken = [
'access_token' => 'atokenvalue',
'client_id' => 'a client id',
'user_id' => 'a user id',
'expires' => 99999999900,
'scope' => 'basicUser withPermission anExtraScope',
];
$test = $this;
$next = function ($request, $response) use ($expectedToken, $test) {
$test->assertSame($expectedToken, $request->getAttribute(Authorization::TOKEN_ATTRIBUTE_KEY));
return $response;
};

$response = $middleware($request, new Response(), $next);

$this->assertSame(200, $response->getStatusCode());
$this->assertSame(
[
'access_token' => 'atokenvalue',
'client_id' => 'a client id',
'user_id' => 'a user id',
'expires' => 99999999900,
'scope' => 'basicUser withPermission anExtraScope',
],
$container['token']
);
$this->assertSame($expectedToken, $container['token']);
}

/**
Expand Down Expand Up @@ -380,22 +379,22 @@ public function invokeWithEmptyScope()

$middleware = new Authorization($server, $container, []);

$next = function ($request, $response) {
$expectedToken = [
'access_token' => 'atokenvalue',
'client_id' => 'a client id',
'user_id' => 'a user id',
'expires' => 99999999900,
'scope' => null,
];
$test = $this;
$next = function ($request, $response) use ($expectedToken, $test) {
$test->assertSame($expectedToken, $request->getAttribute(Authorization::TOKEN_ATTRIBUTE_KEY));
return $response;
};

$middleware($request, new Response(), $next);

$this->assertSame(
[
'access_token' => 'atokenvalue',
'client_id' => 'a client id',
'user_id' => 'a user id',
'expires' => 99999999900,
'scope' => null,
],
$container['token']
);
$this->assertSame($expectedToken , $container['token']);
}

/**
Expand Down Expand Up @@ -466,7 +465,7 @@ public function invokeRetainsContentType()
* @test
* @covers ::__construct
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage $container does not implement \ArrayAccess or \Interop\Container\ContainerInterface
* @expectedExceptionMessage $container does not implement ArrayAccess or contain a 'set' method
*
* @return void
*/
Expand All @@ -476,6 +475,23 @@ public function constructWithInvalidContainer()
new Authorization($oauth2ServerMock, new \StdClass());
}

/**
* Verify middleware cannot be constructed with a pure PSR-11 container.
*
* @test
* @covers ::__construct
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage $container does not implement ArrayAccess or contain a 'set' method
*
* @return void
*/
public function constructWithPSR11Container()
{
$container = $this->getMockBuilder('\\Psr\\Container\\ContainerInterface')->getMock();
$oauth2ServerMock = $this->getMockBuilder('\\OAuth2\\Server')->disableOriginalConstructor()->getMock();
new Authorization($oauth2ServerMock, $container);
}

/**
* Verify middleware can use interop container.
*
Expand Down Expand Up @@ -517,21 +533,21 @@ public function invokeWithInteropContainer()

$middleware = new Authorization($server, $container);

$next = function ($request, $response) {
$expectedToken = [
'access_token' => 'atokenvalue',
'client_id' => 'a client id',
'user_id' => 'a user id',
'expires' => 99999999900,
'scope' => null,
];
$test = $this;
$next = function ($request, $response) use ($expectedToken, $test) {
$test->assertSame($expectedToken, $request->getAttribute(Authorization::TOKEN_ATTRIBUTE_KEY));
return $response;
};

$middleware($request, new Response(), $next);

$this->assertSame(
[
'access_token' => 'atokenvalue',
'client_id' => 'a client id',
'user_id' => 'a user id',
'expires' => 99999999900,
'scope' => null,
],
$container->get('token')
);
$this->assertSame($expectedToken, $container->get('token'));
}
}

0 comments on commit 4a74900

Please sign in to comment.