Skip to content

Commit 481c9ad

Browse files
committed
added required all option to routes to require all scopes for oauth2
1 parent 93e4c6a commit 481c9ad

File tree

3 files changed

+72
-4
lines changed

3 files changed

+72
-4
lines changed

src/Auth/Provider/OAuth2.php

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,13 @@ public function authenticate(Request $request, Route $route)
7979
try {
8080
$this->resource->isValidRequest($this->httpHeadersOnly);
8181

82-
$this->validateRouteScopes($token = $this->resource->getAccessToken(), $route);
82+
$token = $this->resource->getAccessToken();
83+
84+
if ($route->requiresAllScopes()) {
85+
$this->validateAllRouteScopes($token, $route);
86+
} else {
87+
$this->validateAnyRouteScopes($token, $route);
88+
}
8389

8490
return $this->resolveResourceOwner($token);
8591
} catch (OAuthException $exception) {
@@ -106,7 +112,7 @@ protected function resolveResourceOwner(AccessTokenEntity $token)
106112
}
107113

108114
/**
109-
* Validate a routes scopes.
115+
* Validate a route has any scopes.
110116
*
111117
* @param \League\OAuth2\Server\Entity\AccessTokenEntity $token
112118
* @param \Dingo\Api\Routing\Route $route
@@ -115,7 +121,7 @@ protected function resolveResourceOwner(AccessTokenEntity $token)
115121
*
116122
* @return bool
117123
*/
118-
protected function validateRouteScopes(AccessTokenEntity $token, Route $route)
124+
protected function validateAnyRouteScopes(AccessTokenEntity $token, Route $route)
119125
{
120126
$scopes = $route->scopes();
121127

@@ -132,6 +138,29 @@ protected function validateRouteScopes(AccessTokenEntity $token, Route $route)
132138
throw new InvalidScopeException($scope);
133139
}
134140

141+
/**
142+
* Validate a route has all scopes.
143+
*
144+
* @param \League\OAuth2\Server\Entity\AccessTokenEntity $token
145+
* @param \Dingo\Api\Routing\Route $route
146+
*
147+
* @throws \League\OAuth2\Server\Exception\InvalidScopeException
148+
*
149+
* @return bool
150+
*/
151+
protected function validateAllRouteScopes(AccessTokenEntity $token, Route $route)
152+
{
153+
$scopes = $route->scopes();
154+
155+
foreach ($scopes as $scope) {
156+
if (! $token->hasScope($scope)) {
157+
throw new InvalidScopeException($scope);
158+
}
159+
}
160+
161+
return true;
162+
}
163+
135164
/**
136165
* Set the resolver to fetch a user.
137166
*

src/Routing/Route.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,16 @@ public function getScopes()
352352
return $this->scopes;
353353
}
354354

355+
/**
356+
* Check if route requires any or all scopes
357+
*
358+
* @return bool
359+
*/
360+
public function requiresAllScopes()
361+
{
362+
return array_get($this->action, 'requireAll', false);
363+
}
364+
355365
/**
356366
* Get the route authentication providers.
357367
*

tests/Auth/Provider/OAuth2Test.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@ public function testExceptionThrownWhenNoScopesProvided()
3636

3737
$route = m::mock('Dingo\Api\Routing\Route');
3838
$route->shouldReceive('scopes')->once()->andReturn(['foo']);
39+
$route->shouldReceive('requiresAllScopes')->once()->andReturn(false);
3940

4041
$this->provider->authenticate($request, $route);
4142
}
4243

43-
public function testOnlyOneScopeRequiredToValidateCorrectly()
44+
public function testOnlyOneScopeRequiredToValidateCorrectlyIfRequiredAllSetToFalse()
4445
{
4546
$request = Request::create('GET', '/', [], [], [], ['HTTP_AUTHORIZATION' => 'Bearer 12345']);
4647

@@ -62,10 +63,36 @@ public function testOnlyOneScopeRequiredToValidateCorrectly()
6263

6364
$route = m::mock('Dingo\Api\Routing\Route');
6465
$route->shouldReceive('scopes')->once()->andReturn(['foo', 'bar']);
66+
$route->shouldReceive('requiresAllScopes')->once()->andReturn(false);
6567

6668
$this->assertNull($this->provider->authenticate($request, $route));
6769
}
6870

71+
/**
72+
* @expectedException \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
73+
*/
74+
public function testAllScopeRequiredToValidateCorrectlyIfRequiredAllSetToTrue()
75+
{
76+
$request = Request::create('GET', '/', [], [], [], ['HTTP_AUTHORIZATION' => 'Bearer 12345']);
77+
78+
$this->server->shouldReceive('isValidRequest')->once()->andReturn(true);
79+
80+
$token = m::mock('League\OAuth2\Server\Entity\AccessTokenEntity');
81+
$token->shouldReceive('hasScope')->once()->with('foo')->andReturn(true);
82+
$token->shouldReceive('hasScope')->once()->with('bar')->andReturn(false);
83+
$this->server->shouldReceive('getAccessToken')->once()->andReturn($token);
84+
85+
$this->provider->setClientResolver(function ($id) {
86+
//
87+
});
88+
89+
$route = m::mock('Dingo\Api\Routing\Route');
90+
$route->shouldReceive('scopes')->once()->andReturn(['foo', 'bar']);
91+
$route->shouldReceive('requiresAllScopes')->once()->andReturn(true);
92+
93+
$this->provider->authenticate($request, $route);
94+
}
95+
6996
public function testClientIsResolved()
7097
{
7198
$request = Request::create('GET', '/', [], [], [], ['HTTP_AUTHORIZATION' => 'Bearer 12345']);
@@ -87,6 +114,7 @@ public function testClientIsResolved()
87114

88115
$route = m::mock('Dingo\Api\Routing\Route');
89116
$route->shouldReceive('scopes')->once()->andReturn([]);
117+
$route->shouldReceive('requiresAllScopes')->once()->andReturn(false);
90118

91119
$this->assertEquals('foo', $this->provider->authenticate($request, $route));
92120
}
@@ -112,6 +140,7 @@ public function testUserIsResolved()
112140

113141
$route = m::mock('Dingo\Api\Routing\Route');
114142
$route->shouldReceive('scopes')->once()->andReturn([]);
143+
$route->shouldReceive('requiresAllScopes')->once()->andReturn(false);
115144

116145
$this->assertEquals('foo', $this->provider->authenticate($request, $route));
117146
}

0 commit comments

Comments
 (0)