cgross / laraguard
Permissions/roles for Laravel 5
Installs: 7 223
Dependents: 0
Suggesters: 0
Security: 0
Stars: 39
Watchers: 7
Forks: 5
Open Issues: 0
Requires
- laravel/framework: ~5.0
- symfony/yaml: ~2.1
This package is not auto-updated.
Last update: 2024-11-09 16:37:38 UTC
README
Adds a permission system to Laravel 5 using the new integrated authentication mechanism. Instead of protecting routes, it protects the controller and its methods. This way you do not expose protected functionality if you forgot to protect a certain route. Controllers are protected with a simple syntax: ControllerName@MethodName
. If you have a ClientController.php
and want to add a permission called client.edit you would do something like this:
client.edit: - Client@edit - Client@update
Laraguard also supports *
if you want to allow all methods or all controllers:
client.admin: - Client@* site.admin: - "*@*"
Installation
Composer:
Integrate this into your laravel projects composer.json
and execute composer update
:
"require": { "cgross/laraguard": "~1.0", },
Add middleware:
Add this line to the $routeMiddleware
array in app/Http/Kernel.php
:
'laraguard' => 'CGross\Laraguard\Middleware\Permission',
Protect controllers:
For every controller that you want to protect from unauthorized access call the laraguard middleware in the constructor like this:
/** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('laraguard'); }
Extend the User model:
Extend the User
model with a method getPermissions
which returns an array with the users permissions. You might also want to extend the user schema to save permissions in the database. If Laraguard is not flexible enough for your needs you can create a new database table for user roles which then references permissions assigned to those roles. Implement it like you want, just make sure the getPermissions
method exists in the User
model and that it returns an array with permission names.
Take a look at https://github.com/caffeinated/shinobi or https://github.com/romanbican/roles which both allow you to store roles and permissions for your users. Then adapt the getPermissions
method in your user model to retrieve the permissions from caffeinated/shinobi or romanbican/roles.
Permissions
Create an new file resources/config/permissions.yml
with the following content, adapted to your needs:
defaultNoPermissionRoute: /denied debug: true deniedUrlLifetime: 1 controllerActionPermissions: admin: - "*@*" guest: - "Welcome@index" user: - "Client@index" - "Client@show" manager: - "Client@*" customPermissions: customPermission: - /Controller/ secCust: - /Blubb/ testing: appEnv: acceptance debug: true defaultPermissions: null temporaryPermissions: '' temporaryRequestCounter: 0
Note: The default permission for users that are not logged in is guest
.
What happens when the user or guest has no permission?
If the user has no permission for the desired controller method then there are three possibilities:
- The controller has a method named
permissionDenied
. In this case the method is called. This gives you the ability to display custom permission denied views for different controllers or redirect to some other page - The value
defaultNoPermissionRoute
inpermissions.yml
is notNONE
. In this case the request is redirected to this route - Neither a
permissionDenied
nor adefaultNoPermissionRoute
is set: In this case the response will be a501 Permission Denied
error page.
Testing
When testing an app you might want to set some defaultPermissions for testing mode. Those permissions will only work if you test with the same APP_ENV
that is specified in appEnv
. You can do this with the following entries in permissions.yml
:
testing: appEnv: acceptance defaultPermissions: guest,admin,someOtherPermName
If you want to set different Permission for different testcases then you need to add the LaraguardServiceProvider
in config/app.php
:
'CGross\Laraguard\Providers\LaraguardServiceProvider',
After that you can get a Laraguard instance from the IoC Container:
$laraguard = \App::make('Laraguard'); // Set some permissions $laraguard->setTemporaryPermissions(['guest','admin','someOtherPermName']); // Your test case goes here ... // Clean up afterwards $laraguard->resetTemporaryPermissions() // Or only set temporary permission for X requests // In this case only the next two requests will have temporary permissions $laraguard->setTemporaryPermissions(['guest','admin'], 2); // Get temporary permissions (might be handy to merge with user permissions during tests) $laraguard->getTemporaryPermissions()
Behat
If you are using Behat
then try the Laraguard trait:
use CGross\Laraguard\Behat\Laraguard; class FeatureContext extends MinkContext implements Context, SnippetAcceptingContext { // Includes the trait use Laraguard; // Your tests go here // Access laraguard like this: // self::$laraguard->setTemporaryPermissions($permissionArray, $lifetime); // self::$laraguard->resetTemporaryPermissions(); }
This trait will automatically clear all temporary permissions after each scenario and if you need you can use self::$laraguard
in your tests to set or reset permissions. In your behat features you will have the following new expressions available:
Scenario: X test permission
Given I have the permission "admin"
Scenario: XY test permission
Given I have the permissions "admin,guest,someOtherPerm"
Scenario: XYZ test permission
Given I have the permissions "admin,guest,someOtherPerm" for 2 requests
Debugging
You can now enabled debugging in your permissions.yml
. This will print debug output to your laravel log (usually in storage/log/laravel-YYYY-MM-DD.log
).
...
[Date] acceptance.INFO: [Laraguard] REQUEST - ControllerPath: App\Http\Controllers\ClientController@index
[Date] acceptance.INFO: [Laraguard] ALLOW - Allowed permissions: admin,customPermission - User: admin
[Date] acceptance.INFO: [Laraguard] REQUEST - ControllerPath: App\Http\Controllers\Admin\UserAdminController@getCreate
[Date] acceptance.INFO: [Laraguard] TESTING permissions: admin, testing
[Date] acceptance.INFO: [Laraguard] ALLOW - Allowed permissions: admin,customPermission - User: guest,admin,testing
[Date] acceptance.INFO: [Laraguard] REQUEST - ControllerPath: App\Http\Controllers\Admin\UserAdminController@getCreate
[Date] acceptance.INFO: [Laraguard] DENY - Allowed permissions: admin,customPermission - User: guest
[Date] acceptance.INFO: [Laraguard] DENY - with defaultNoPermissionRoute: /denied
...
Redirect after login / get last denied page
Laraguard stores the path of the last denied page in a session var (laraguard_lastDenied
). This session var will be cleared after X requests (default is two requests). You can change this under deniedUrlLifetime
in your permissions.yml
.
Add a login form to your denied page. To redirect a user after login, modify the redirectPath
method of your AuthController
:
public function redirectPath() { // Redirect after denied request? if(\Session::has('laraguard_lastDenied')) { return \Session::get('laraguard_lastDenied'); } // Redirect to default path after login/register return property_exists($this, 'redirectTo') ? $this->redirectTo : '/home'; }
Changelog
1.1.0:
- Store last denied url to make a redirect after login feature possible
- Give access to testing permissions
getTemporaryPermissions()
1.0.0:
- Added testing capabilities
- Support for Behat
- Added debugging (see permission.yml in README)
0.1.0: Initial release
Breaking changes
Version v1.0.0
changed modelActionPermissions
to controllerActionPermissions
in permissions.yml
.