Skip to content

Commit 5969617

Browse files
authored
Merge pull request #696 from stevenmaguire/sm-add-guarded-attribute-support
Add support to blacklist mass assignment of specific provider properties
2 parents 6e95e46 + 63c505a commit 5969617

File tree

4 files changed

+136
-18
lines changed

4 files changed

+136
-18
lines changed

src/Provider/AbstractProvider.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use League\OAuth2\Client\Token\AccessToken;
2626
use League\OAuth2\Client\Token\AccessTokenInterface;
2727
use League\OAuth2\Client\Tool\ArrayAccessorTrait;
28+
use League\OAuth2\Client\Tool\GuardedPropertyTrait;
2829
use League\OAuth2\Client\Tool\QueryBuilderTrait;
2930
use League\OAuth2\Client\Tool\RequestFactory;
3031
use Psr\Http\Message\RequestInterface;
@@ -39,6 +40,7 @@
3940
abstract class AbstractProvider
4041
{
4142
use ArrayAccessorTrait;
43+
use GuardedPropertyTrait;
4244
use QueryBuilderTrait;
4345

4446
/**
@@ -109,11 +111,9 @@ abstract class AbstractProvider
109111
*/
110112
public function __construct(array $options = [], array $collaborators = [])
111113
{
112-
foreach ($options as $option => $value) {
113-
if (property_exists($this, $option)) {
114-
$this->{$option} = $value;
115-
}
116-
}
114+
// We'll let the GuardedPropertyTrait handle mass assignment of incoming
115+
// options, skipping any blacklisted properties defined in the provider
116+
$this->fillProperties($options);
117117

118118
if (empty($collaborators['grantFactory'])) {
119119
$collaborators['grantFactory'] = new GrantFactory();

src/Tool/GuardedPropertyTrait.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
/**
3+
* This file is part of the league/oauth2-client library
4+
*
5+
* For the full copyright and license information, please view the LICENSE
6+
* file that was distributed with this source code.
7+
*
8+
* @copyright Copyright (c) Alex Bilbie <[email protected]>
9+
* @license http://opensource.org/licenses/MIT MIT
10+
* @link http://thephpleague.com/oauth2-client/ Documentation
11+
* @link https://packagist.org/packages/league/oauth2-client Packagist
12+
* @link https://github.com/thephpleague/oauth2-client GitHub
13+
*/
14+
15+
namespace League\OAuth2\Client\Tool;
16+
17+
/**
18+
* Provides support for blacklisting explicit properties from the
19+
* mass assignment behavior.
20+
*/
21+
trait GuardedPropertyTrait
22+
{
23+
/**
24+
* The properties that aren't mass assignable.
25+
*
26+
* @var array
27+
*/
28+
protected $guarded = [];
29+
30+
/**
31+
* Attempts to mass assign the given options to explicitly defined properties,
32+
* skipping over any properties that are defined in the guarded array.
33+
*
34+
* @param array $options
35+
* @return mixed
36+
*/
37+
protected function fillProperties(array $options = [])
38+
{
39+
if (isset($options['guarded'])) {
40+
unset($options['guarded']);
41+
}
42+
43+
foreach ($options as $option => $value) {
44+
if (property_exists($this, $option) && !$this->isGuarded($option)) {
45+
$this->{$option} = $value;
46+
}
47+
}
48+
}
49+
50+
/**
51+
* Returns current guarded properties.
52+
*
53+
* @return array
54+
*/
55+
public function getGuarded()
56+
{
57+
return $this->guarded;
58+
}
59+
60+
/**
61+
* Determines if the given property is guarded.
62+
*
63+
* @param string $property
64+
* @return bool
65+
*/
66+
public function isGuarded($property)
67+
{
68+
return in_array($property, $this->getGuarded());
69+
}
70+
}

test/src/Provider/AbstractProviderTest.php

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace League\OAuth2\Client\Test\Provider;
44

5+
use UnexpectedValueException;
56
use Eloquent\Liberator\Liberator;
67
use Eloquent\Phony\Phpunit\Phony;
78
use GuzzleHttp\Exception\BadResponseException;
@@ -10,6 +11,7 @@
1011
use League\OAuth2\Client\Test\Provider\Fake as MockProvider;
1112
use League\OAuth2\Client\Grant\AbstractGrant;
1213
use League\OAuth2\Client\Grant\GrantFactory;
14+
use League\OAuth2\Client\Grant\Exception\InvalidGrantException;
1315
use League\OAuth2\Client\Token\AccessToken;
1416
use League\OAuth2\Client\Token\AccessTokenInterface;
1517
use League\OAuth2\Client\Tool\RequestFactory;
@@ -35,19 +37,15 @@ protected function setUp()
3537
]);
3638
}
3739

38-
/**
39-
* @expectedException League\OAuth2\Client\Grant\Exception\InvalidGrantException
40-
*/
4140
public function testInvalidGrantString()
4241
{
42+
$this->expectException(InvalidGrantException::class);
4343
$this->provider->getAccessToken('invalid_grant', ['invalid_parameter' => 'none']);
4444
}
4545

46-
/**
47-
* @expectedException League\OAuth2\Client\Grant\Exception\InvalidGrantException
48-
*/
4946
public function testInvalidGrantObject()
5047
{
48+
$this->expectException(InvalidGrantException::class);
5149
$grant = new \StdClass();
5250
$this->provider->getAccessToken($grant, ['invalid_parameter' => 'none']);
5351
}
@@ -398,11 +396,9 @@ public function testErrorResponsesCanBeCustomizedAtTheProvider()
398396
);
399397
}
400398

401-
/**
402-
* @expectedException \League\OAuth2\Client\Provider\Exception\IdentityProviderException
403-
*/
404399
public function testClientErrorTriggersProviderException()
405400
{
401+
$this->expectException(IdentityProviderException::class);
406402
$provider = new MockProvider([
407403
'clientId' => 'mock_client_id',
408404
'clientSecret' => 'mock_secret',
@@ -600,29 +596,34 @@ public function parseResponseProvider()
600596
/**
601597
* @dataProvider parseResponseProvider
602598
*/
603-
public function testParseResponse($body, $type, $parsed)
599+
public function testParseResponse($body, $type, $parsed, $statusCode = 200)
604600
{
605601
$stream = Phony::mock(StreamInterface::class);
606602
$stream->__toString->returns($body);
607603

608604
$response = Phony::mock(ResponseInterface::class);
609605
$response->getBody->returns($stream->get());
610606
$response->getHeader->with('content-type')->returns($type);
607+
$response->getStatusCode->returns($statusCode);
611608

612609
$method = $this->getMethod(AbstractProvider::class, 'parseResponse');
613610
$result = $method->invoke($this->provider, $response->get());
614611

615612
$this->assertEquals($parsed, $result);
616613
}
617614

618-
/**
619-
* @expectedException UnexpectedValueException
620-
*/
621615
public function testParseResponseJsonFailure()
622616
{
617+
$this->expectException(UnexpectedValueException::class);
623618
$this->testParseResponse('{a: 1}', 'application/json', null);
624619
}
625620

621+
public function testParseResponseNonJsonFailure()
622+
{
623+
$this->expectException(UnexpectedValueException::class);
624+
$this->testParseResponse('<xml></xml>', 'application/xml', null, 500);
625+
}
626+
626627
public function getAppendQueryProvider()
627628
{
628629
return [
@@ -676,6 +677,31 @@ public function testDefaultPrepareAccessTokenResponse()
676677
$this->assertEquals($result['user_id'], $newResult['resource_owner_id']);
677678
}
678679

680+
public function testGuardedProperties()
681+
{
682+
$options = [
683+
'clientId' => 'mock_client_id',
684+
'clientSecret' => 'mock_secret',
685+
'redirectUri' => 'none',
686+
'skipMeDuringMassAssignment' => 'bar',
687+
'guarded' => 'foo'
688+
];
689+
690+
$provider = new Fake\ProviderWithGuardedProperties($options);
691+
692+
$this->assertAttributeNotEquals(
693+
$options['skipMeDuringMassAssignment'],
694+
'skipMeDuringMassAssignment',
695+
$provider
696+
);
697+
698+
$this->assertAttributeNotEquals(
699+
$options['guarded'],
700+
'guarded',
701+
$provider
702+
);
703+
}
704+
679705
public function testPrepareAccessTokenResponseWithDotNotation()
680706
{
681707
$provider = Phony::partialMock(Fake\ProviderWithAccessTokenResourceOwnerId::class);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace League\OAuth2\Client\Test\Provider\Fake;
4+
5+
use League\OAuth2\Client\Test\Provider\Fake as MockProvider;
6+
7+
class ProviderWithGuardedProperties extends MockProvider
8+
{
9+
/**
10+
* The properties that aren't mass assignable.
11+
*
12+
* @var array
13+
*/
14+
protected $guarded = ['skipMeDuringMassAssignment'];
15+
16+
/**
17+
* Throwaway property that shouldn't be mass assigned.
18+
*
19+
* @var string
20+
*/
21+
protected $skipMeDuringMassAssignment = 'foo';
22+
}

0 commit comments

Comments
 (0)