From 8df06ae2c35bfd986894cf5bf9164ea236c3020d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Wed, 17 Mar 2021 17:47:55 +0100 Subject: [PATCH] implicit access --- README.md | 57 +++++++++++++++++-- composer.json | 2 +- src/BlockFrontendAccess.php | 12 ++++ src/WithImplicitAccess.php | 23 ++++++++ ...eAccessTest.php => ExplicitAccessTest.php} | 21 ++----- ...estComponent.php => ExplicitComponent.php} | 2 +- tests/ImplicitAccessTest.php | 46 +++++++++++++++ tests/ImplicitComponent.php | 27 +++++++++ tests/TestCase.php | 16 ++++++ 9 files changed, 184 insertions(+), 22 deletions(-) create mode 100644 src/BlockFrontendAccess.php create mode 100644 src/WithImplicitAccess.php rename tests/{LivewireAccessTest.php => ExplicitAccessTest.php} (61%) rename tests/{TestComponent.php => ExplicitComponent.php} (90%) create mode 100644 tests/ImplicitAccessTest.php create mode 100644 tests/ImplicitComponent.php create mode 100644 tests/TestCase.php diff --git a/README.md b/README.md index 0c31a13..7c62098 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,15 @@ -# Explicit property/method access for Livewire +# Livewire Access This package adds PHP 8.0 attribute support to Livewire. In specific, the attributes are used for flagging component properties and methods as *frontend-accessible*. -Components which implement the trait provided by this package will implicitly deny access to all properties and methods if they don't have the `#[FrontendAccess]` attribute, regardless of their visibility. +The package ships with two pairs of traits and attributes. One for *explicit* access, and one for *implicit* access. + +- Components which implement the trait for **explicit** access will *deny* access to all properties and methods if they don't have the `#[FrontendAccess]` attribute. +- Components which implement the trait for **implicit** access will *allow* access unless the component has the `#[BlockFrontendAccess]` attribute. + +This acts as a layer on top of Livewire's logic for distinguishing public properties, but it gives you the ability to manually make changes when you need more control than property/method visibility. + +The trait for only allowing explicit access can also be useful to prevent accidentally making methods `public` when it's not needed, which has the potential to lead to security issues. This can be especially useful on teams with junior engineers who don't yet have a full understanding of Livewire's internals, but can be very productive with it. ## Installation @@ -12,6 +19,12 @@ composer require leanadmin/livewire-access ## Usage +This package doesn't make any changes to your existing code. Components which don't implement either one of its traits will not be affected. + +### Explicit access + +To enable the explicit access mode, i.e. only enable access to properties/methods that explicitly allow it, use the `WithExplicitAccess` trait. + ```php use Livewire\Component; use Lean\LivewireAccess\WithExplicitAccess; @@ -34,16 +47,50 @@ class MyComponent extends Component } #[FrontendAccess] - public function secretMethod() + public function publicMethod() { // This method allows frontend access } } ``` -The properties still need to be `public` to be accessible. +### Implicit access -The thrown exceptions are identical to those that Livewire would throw if the properties/methods were not public. +To enable the implicit access mode, i.e. keep using the same mode , use the `WithExplicitAccess` trait. + +```php +use Livewire\Component; +use Lean\LivewireAccess\WithImplicitAccess; +use Lean\LivewireAccess\FrontendAccess; + +class MyComponent extends Component +{ + // Use the trait on your component to enable this functionality + use WithImplicitAccess; + + // This property allows frontend access + public string $accessible; + + #[BlockFrontendAccess] + public string $inaccessible; // This property blocks frontend access + + public function publicMethod() + { + // This method allows frontend access + } + + #[BlockFrontendAccess] + public function secretMethod() + { + // This method blocks frontend access + } +} +``` + +### Details + +- The properties still need to be `public` to be accessible. +- The thrown exceptions are identical to those that Livewire would throw if the properties/methods were not public. ## Development diff --git a/composer.json b/composer.json index 3e2d91d..f4919c8 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "leanadmin/livewire-access", - "description": "", + "description": "Control frontend access to properties/methods in Livewire using PHP 8 attributes.", "type": "library", "license": "MIT", "authors": [ diff --git a/src/BlockFrontendAccess.php b/src/BlockFrontendAccess.php new file mode 100644 index 0000000..f652e5d --- /dev/null +++ b/src/BlockFrontendAccess.php @@ -0,0 +1,12 @@ +getAttributes(BlockFrontendAccess::class)) === 0; + } + + public function propertyIsPublicAndNotDefinedOnBaseClass($propertyName) + { + return parent::propertyIsPublicAndNotDefinedOnBaseClass($propertyName) + && count((new ReflectionProperty($this, $propertyName))->getAttributes(BlockFrontendAccess::class)) === 0; + } +} diff --git a/tests/LivewireAccessTest.php b/tests/ExplicitAccessTest.php similarity index 61% rename from tests/LivewireAccessTest.php rename to tests/ExplicitAccessTest.php index caf917e..ec8ac90 100644 --- a/tests/LivewireAccessTest.php +++ b/tests/ExplicitAccessTest.php @@ -5,49 +5,40 @@ namespace Lean\LivewireAccess\Tests; use Livewire\Exceptions\NonPublicComponentMethodCall; use Livewire\Exceptions\PublicPropertyNotFoundException; use Livewire\Livewire; -use Livewire\LivewireServiceProvider; -use Orchestra\Testbench\TestCase as TestbenchTestCase; -class LivewireAccessTest extends TestbenchTestCase +class ExplicitAccessTest extends TestCase { - protected function getPackageProviders($app) - { - return [ - LivewireServiceProvider::class, - ]; - } - /** @test */ public function public_properties_are_not_accessible_by_default() { $this->expectException(PublicPropertyNotFoundException::class); - Livewire::test(TestComponent::class) + Livewire::test(ExplicitComponent::class) ->call('$set', 'foo', 'xxx'); } /** @test */ public function public_properties_can_be_explicitly_accessible() { - Livewire::test(TestComponent::class) + Livewire::test(ExplicitComponent::class) ->call('$set', 'bar', 'xxx'); // No exception } /** @test */ - public function public_methods_are_not_acccessible_by_default() + public function public_methods_are_not_accessible_by_default() { $this->expectException(NonPublicComponentMethodCall::class); - Livewire::test(TestComponent::class) + Livewire::test(ExplicitComponent::class) ->call('abc'); } /** @test */ public function public_methods_can_be_explicitly_accessible() { - Livewire::test(TestComponent::class) + Livewire::test(ExplicitComponent::class) ->call('def'); // No exception diff --git a/tests/TestComponent.php b/tests/ExplicitComponent.php similarity index 90% rename from tests/TestComponent.php rename to tests/ExplicitComponent.php index 92501e6..9d04401 100644 --- a/tests/TestComponent.php +++ b/tests/ExplicitComponent.php @@ -6,7 +6,7 @@ use Lean\LivewireAccess\WithExplicitAccess; use Lean\LivewireAccess\FrontendAccess; use Livewire\Component; -class TestComponent extends Component +class ExplicitComponent extends Component { use WithExplicitAccess; diff --git a/tests/ImplicitAccessTest.php b/tests/ImplicitAccessTest.php new file mode 100644 index 0000000..236b915 --- /dev/null +++ b/tests/ImplicitAccessTest.php @@ -0,0 +1,46 @@ +call('$set', 'foo', 'xxx'); + } + + /** @test */ + public function public_properties_can_be_explicitly_blocked() + { + $this->expectException(PublicPropertyNotFoundException::class); + + Livewire::test(ImplicitComponent::class) + ->call('$set', 'bar', 'xxx'); + } + + /** @test */ + public function public_methods_are_accessible_by_default() + { + Livewire::test(ImplicitComponent::class) + ->call('abc'); + + // No exception + } + + /** @test */ + public function public_methods_can_be_explicitly_blocked() + { + $this->expectException(NonPublicComponentMethodCall::class); + + Livewire::test(ImplicitComponent::class) + ->call('def'); + } +} diff --git a/tests/ImplicitComponent.php b/tests/ImplicitComponent.php new file mode 100644 index 0000000..a87d3ea --- /dev/null +++ b/tests/ImplicitComponent.php @@ -0,0 +1,27 @@ +