Skip to content

Commit 2582d2a

Browse files
authored
Add support for inherited scopes when limiting scopes on clients (#1683)
1 parent dd3f081 commit 2582d2a

File tree

4 files changed

+75
-22
lines changed

4 files changed

+75
-22
lines changed

src/Client.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
class Client extends Model
1111
{
1212
use HasFactory;
13+
use ResolvesInheritedScopes;
1314

1415
/**
1516
* The database table used by the model.
@@ -163,7 +164,21 @@ public function skipsAuthorization()
163164
*/
164165
public function hasScope($scope)
165166
{
166-
return ! is_array($this->scopes) || in_array($scope, $this->scopes);
167+
if (! is_array($this->scopes)) {
168+
return true;
169+
}
170+
171+
$scopes = Passport::$withInheritedScopes
172+
? $this->resolveInheritedScopes($scope)
173+
: [$scope];
174+
175+
foreach ($scopes as $scope) {
176+
if (in_array($scope, $this->scopes)) {
177+
return true;
178+
}
179+
}
180+
181+
return false;
167182
}
168183

169184
/**

src/ResolvesInheritedScopes.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Laravel\Passport;
4+
5+
trait ResolvesInheritedScopes
6+
{
7+
/**
8+
* Resolve all possible scopes.
9+
*
10+
* @param string $scope
11+
* @return array
12+
*/
13+
protected function resolveInheritedScopes($scope)
14+
{
15+
$parts = explode(':', $scope);
16+
17+
$partsCount = count($parts);
18+
19+
$scopes = [];
20+
21+
for ($i = 1; $i <= $partsCount; $i++) {
22+
$scopes[] = implode(':', array_slice($parts, 0, $i));
23+
}
24+
25+
return $scopes;
26+
}
27+
}

src/Token.php

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
class Token extends Model
88
{
9+
use ResolvesInheritedScopes;
10+
911
/**
1012
* The database table used by the model.
1113
*
@@ -94,27 +96,6 @@ public function can($scope)
9496
return false;
9597
}
9698

97-
/**
98-
* Resolve all possible scopes.
99-
*
100-
* @param string $scope
101-
* @return array
102-
*/
103-
protected function resolveInheritedScopes($scope)
104-
{
105-
$parts = explode(':', $scope);
106-
107-
$partsCount = count($parts);
108-
109-
$scopes = [];
110-
111-
for ($i = 1; $i <= $partsCount; $i++) {
112-
$scopes[] = implode(':', array_slice($parts, 0, $i));
113-
}
114-
115-
return $scopes;
116-
}
117-
11899
/**
119100
* Determine if the token is missing a given scope.
120101
*

tests/Unit/BridgeScopeRepositoryTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313

1414
class BridgeScopeRepositoryTest extends TestCase
1515
{
16+
protected function tearDown(): void
17+
{
18+
Passport::$withInheritedScopes = false;
19+
}
20+
1621
public function test_invalid_scopes_are_removed()
1722
{
1823
Passport::tokensCan([
@@ -77,6 +82,31 @@ public function test_scopes_disallowed_for_client_are_removed()
7782
$this->assertEquals([$scope1], $scopes);
7883
}
7984

85+
public function test_scopes_disallowed_for_client_are_removed_but_inherited_scopes_are_not()
86+
{
87+
Passport::$withInheritedScopes = true;
88+
89+
Passport::tokensCan([
90+
'scope-1' => 'description',
91+
'scope-1:limited-access' => 'description',
92+
'scope-2' => 'description',
93+
]);
94+
95+
$client = Mockery::mock(ClientModel::class)->makePartial();
96+
$client->scopes = ['scope-1'];
97+
98+
$clients = Mockery::mock(ClientRepository::class);
99+
$clients->shouldReceive('findActive')->withAnyArgs()->andReturn($client);
100+
101+
$repository = new ScopeRepository($clients);
102+
103+
$scopes = $repository->finalizeScopes(
104+
[$scope1 = new Scope('scope-1:limited-access'), new Scope('scope-2')], 'client_credentials', new Client('id', 'name', 'http://localhost'), 1
105+
);
106+
107+
$this->assertEquals([$scope1], $scopes);
108+
}
109+
80110
public function test_superuser_scope_cant_be_applied_if_wrong_grant()
81111
{
82112
Passport::tokensCan([

0 commit comments

Comments
 (0)