Skip to content

Commit

Permalink
Add support for inherited scopes when limiting scopes on clients (#1683)
Browse files Browse the repository at this point in the history
  • Loading branch information
axlon authored Aug 29, 2023
1 parent dd3f081 commit 2582d2a
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 22 deletions.
17 changes: 16 additions & 1 deletion src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
class Client extends Model
{
use HasFactory;
use ResolvesInheritedScopes;

/**
* The database table used by the model.
Expand Down Expand Up @@ -163,7 +164,21 @@ public function skipsAuthorization()
*/
public function hasScope($scope)
{
return ! is_array($this->scopes) || in_array($scope, $this->scopes);
if (! is_array($this->scopes)) {
return true;
}

$scopes = Passport::$withInheritedScopes
? $this->resolveInheritedScopes($scope)
: [$scope];

foreach ($scopes as $scope) {
if (in_array($scope, $this->scopes)) {
return true;
}
}

return false;
}

/**
Expand Down
27 changes: 27 additions & 0 deletions src/ResolvesInheritedScopes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Laravel\Passport;

trait ResolvesInheritedScopes
{
/**
* Resolve all possible scopes.
*
* @param string $scope
* @return array
*/
protected function resolveInheritedScopes($scope)
{
$parts = explode(':', $scope);

$partsCount = count($parts);

$scopes = [];

for ($i = 1; $i <= $partsCount; $i++) {
$scopes[] = implode(':', array_slice($parts, 0, $i));
}

return $scopes;
}
}
23 changes: 2 additions & 21 deletions src/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

class Token extends Model
{
use ResolvesInheritedScopes;

/**
* The database table used by the model.
*
Expand Down Expand Up @@ -94,27 +96,6 @@ public function can($scope)
return false;
}

/**
* Resolve all possible scopes.
*
* @param string $scope
* @return array
*/
protected function resolveInheritedScopes($scope)
{
$parts = explode(':', $scope);

$partsCount = count($parts);

$scopes = [];

for ($i = 1; $i <= $partsCount; $i++) {
$scopes[] = implode(':', array_slice($parts, 0, $i));
}

return $scopes;
}

/**
* Determine if the token is missing a given scope.
*
Expand Down
30 changes: 30 additions & 0 deletions tests/Unit/BridgeScopeRepositoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@

class BridgeScopeRepositoryTest extends TestCase
{
protected function tearDown(): void
{
Passport::$withInheritedScopes = false;
}

public function test_invalid_scopes_are_removed()
{
Passport::tokensCan([
Expand Down Expand Up @@ -77,6 +82,31 @@ public function test_scopes_disallowed_for_client_are_removed()
$this->assertEquals([$scope1], $scopes);
}

public function test_scopes_disallowed_for_client_are_removed_but_inherited_scopes_are_not()
{
Passport::$withInheritedScopes = true;

Passport::tokensCan([
'scope-1' => 'description',
'scope-1:limited-access' => 'description',
'scope-2' => 'description',
]);

$client = Mockery::mock(ClientModel::class)->makePartial();
$client->scopes = ['scope-1'];

$clients = Mockery::mock(ClientRepository::class);
$clients->shouldReceive('findActive')->withAnyArgs()->andReturn($client);

$repository = new ScopeRepository($clients);

$scopes = $repository->finalizeScopes(
[$scope1 = new Scope('scope-1:limited-access'), new Scope('scope-2')], 'client_credentials', new Client('id', 'name', 'http://localhost'), 1
);

$this->assertEquals([$scope1], $scopes);
}

public function test_superuser_scope_cant_be_applied_if_wrong_grant()
{
Passport::tokensCan([
Expand Down

0 comments on commit 2582d2a

Please sign in to comment.