How to Use Permissions in Sentry for Laravel application - php

I need to use Sentry 2.1 in a Laravel application, I read this document
https://cartalyst.com/manual/sentry/2.1
what I really need to have is some groups and assign some permissions to each group and then assign those groups to the users.
take this as an example (which I took from the same link):
I register a user with following detaiks
Sentry::register(array(
'email' => 'john.doe#example.com',
'password' => 'foobar',
'activated' => true,
));
Then I register a group with the following details:
$group = Sentry::createGroup(array(
'name' => 'Moderator',
'permissions' => array(
'admin' => 1,
'writers' => 1,
),
));
And then I assigned the group to the user
The Question:
Can someone provide me with a piece of code that helped me through how I should modify routes.php and add filters to it, so that the filters will apply on permissions and not the groups.
Route::group(array('before' => 'admin'), function()
{
Route::controller('admin','adminController');
});
Route::group(array('before' => 'mod'), function()
{
Route::controller('cruds','crudController');
});
For example users with admin permissions can only see the adminController links

Checking permissions is done via the Sentry hasAccess() method. You can either create multiple filters to take specific actions for different permission checks, or you can use a generic filter which takes the permission as a parameter and check on that. Below is a generic "hasAccess" filter, to which you pass the permission for which to check.
Filter:
Route::filter('hasAccess', function ($route, $request, $value) {
try {
// get the logged in user
$user = Sentry::getUser();
// check the user against the requested permission
if (!$user->hasAccess($value)) {
// action to take if the user doesn't have permission
return Redirect::home();
}
} catch (Cartalyst\Sentry\Users\UserNotFoundException $e) {
// action to take if the user is not logged in
return Redirect::guest(route('login'));
}
});
Routes:
Route::group(array('before' => 'hasAccess:admin'), function() {
Route::controller('admin','adminController');
});
Route::group(array('before' => 'hasAccess:mod'), function() {
Route::controller('cruds','crudController');
});

Related

How to use multiple Middleware groups on Laravel?

I'm trying to restrict the access of routes to only some types of users in my site that I'm writing with Laravel 5.7, right now I'm trying to do it with middlewares.
For each user's level I have a middleware with this code(with a variation on the type):
public function handle($request, Closure $next)
{
if(Auth::user()->type==3)
return $next($request);
else
return redirect()->route('dashboard');
}
And in the kernel.php file, I have them written like this:
protected $routeMiddleware = [
...
'teacher' => \App\Http\Middleware\RedirectIfTeacher::class,
...
]
In my application each user has a level, which starts from 1 to 5, but each level has individual views and some shared views, but I can't manage to redirect views for more than just one type of user because I can't make them work when I use more than one middlewares on a route (or routes) that are shared with more than two types of users.
When I try it justs ignores the second or more middlewares and redirects to the route dashboard which is the route for redirecting if the type of user can't enter the desired view.
Right now I've tried with this code:
Route::group(['middleware' => ['administrator','teacher','student']], function(){
And with this code:
Route::group(['middleware' => ['administrator' OR 'teacher' OR 'student']], function(){
Also I tried with this style:
Route::group(['middleware' => ['administrator|teacher|student']], function(){
Without getting any luck, is there anything what am I doing wrong? or is there a better way to do what I'm trying to achieve, thanks in advance!.
I'm using below code and it worked:
Route::group(['middleware' => ['administrator','teacher','student']], function() {});
1 In the kernel.php file, have you got all of the keys assigned with your middlewares ? For example:
protected $routeMiddleware = [
...
'administrator' => \App\Http\Middleware\RedirectIfAdmin::class,
'teacher' => \App\Http\Middleware\RedirectIfTeacher::class,
'student' => \App\Http\Middleware\RedirectIfStudent::class,
...
]
2 Try to check the user vars before the if in your handle().
dd(Auth::user()->type);
You need to pass an array to it I guess
Route::group(['middleware' => ['administrator','teacher','student']], function() {});
If that doesn't work you have to split them I guess
Route::group(['middleware' => 'administrator'], function () {
Route::group([ 'middleware' => 'teacher'], function() {
Route::group([ 'middleware' => 'student'], function() {
});
});
});

Middleware overriding other middleware in Laravel

I’m currently building my first larval app.
I’ve got two user types: buyers and sellers. However, they both have access to the same routes, but they need to be served up different templates and data. For example, both would have access to /home, but see something completely different.
I know I can use an if statement within the route, but that’s getting messy. To try and fix this, I’ve made two separate middlewares using two different route groups. Each middleware checks the user for permission, and logs them out if they don't have it, like so:
if ( Auth::user()->role !== 'buyer' ) {
return redirect( 'login' );
}
My goal:
If the user 'isBuyer', it only reads that middleware route group.
If the user 'isSeller', it only reads that middleware group.
I was hoping to use both in tandem:
// this will be triggered if they have the role 'buyer'
Route::group(['middleware' => 'isBuyer'], function () {
Route::get( '/', function ( ) {
return view( '/home-seller' );
});
});
//this will be triggered if they have the role 'seller'
Route::group(['middleware' => 'isSeller'], function () {
Route::get( '/', function ( ) {
return view( '/home-buyer' );
});
});
But it never reads the second group. It logs me out as a result of failing the auth test on the first group.
I assume this is because it's applying the middleware filter before it finishes reading the routes.php file. What's the best to deal with using the same routes for different user types?
You can use something like this:-
$middleware = '';
if ( Auth::user()->role == 'buyer' ) {
$middleware = 'isBuyer';
}
elseif(Auth::user()->role == 'seller') {
$middleware = 'isSeller';
}
else{
return redirect( 'login' );
}
Route::group(['middleware' => $middleware], function () {
Route::get( '/', function ( ) {
return view( '/home-seller' );
});
});
How about use a single route and render from the controller (returning a view) depending the role using "if blocks" for each role. Can you try that.
For this requirement, I think you should check the user role in your app controller/parent controller and depending on that role, redirect the user to their respective actions.

Laravel 5 : Restrict access to controllers by User group

I've started learning Laravel 5.1 and so far I'm liking it! But there is one thing I don't get yet..
In my previous project I had 2 specific controllers (eg: "normal", "extended") which , after a successfull login, were called based on the Users user_group from the database.
If "Foo.Bar" enters his valid credentials and has the group normal he is redirected to NormalControler. Since I wasn't using any framework I restricted access to the other group by setting a $_SESSION with the group and checking it. So if another group tried to access that controller he got redirected.
How would this be achievable in Laravel 5? So far I have a controller which is callable without an Authentication and one restricted by this code in routes.php :
// All routes in the group are protected, only authed user are allowed to access them
Route::group(array('before' => 'auth'), function() {
// TO-DO : Seperate Controller access
});
And the login looks like this :
public function performLogin()
{
$logindata = array(
'username' => Input::get('user_name'),
'password' => Input::get('user_pass')
);
if( Auth::attempt( $logindata ) ){
// return \Redirect::to( check group and access this controller based on it);
}
else {
// TO-DO : Redirect back and show error message
dd('Login failed!');
}
}
----- EDIT -----
I've run the artisan command and made this middleware as you suggested :
namespace App\Http\Middleware;
use Closure;
use Request;
class GroupPermissions
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next, $group)
{
// Check User Group Permissions
if( $request->user()->group === $group ){
// Continue the request
return $next($request);
}
// Redirect
return redirect('restricted');
}
}
and edited this line into Kernel.php into $routeMiddleware :
'group.perm' => \App\Http\Middleware\GroupPermissions::class
I think this is done right so far, correct me if I'm wrong! Could I then do something like this to restrict the controllers?
Route::group(array('before' => 'auth'), function() {
Route::group( ['middleware' => 'group.perm', 'group' => 'normal'], function(){
Route::get('/normal/index', 'DummyNormalController#index');
});
Route::group( ['middleware' => 'group.perm', 'group' => 'extended'], function(){
Route::get('/extended/index', 'DummyExtendedController#index');
});
});
Ok, here is what you might do. Once user is logged in, you would check his credentials, get his user_group and decide what controller he should be redirected to.
if( Auth::attempt( $logindata ) ){
$user = Auth::user();
if ($user->inGroup('normal')) {
return redirect()->route('normal_controllers_named_route');
}
return redirect()->route('extended_controllers_named_route');
}
return redirect()->back()->withFlashMessage('don\'t get me wrong');
This will handle right routing after logging in.
The next portion where you need to protect you routes from unwanted user groups may be achieved with middlewares.
do an artisan command php artisan make:middleware ShouldBeInGroup
go to app/http/Kernel.php and add your new middleware to the routeMiddleware array. Key of the item might be anything you like. Let's call in inGroup. So: 'inGroup' => 'App\Http\Middleware\ShouldBeInGroup'
Now, in your controller, in constructor, you are able to call this middleware
$this->middleware('inGroup:extended'); //we also passing the name of the group
at lastly, work on the our middleware. Open newly created ShouldBeInGroup class and edit the handle method.
public function handle($request, Closure $next, $groupName)
{
if (Auth::check() && Auth::user()->inGroup($groupName)) {
return $next($request);
}
return redirect('/');
}
And finally you should work on inGroup method, that should return true of false. I assume that you have user_group field your users table. Then in your User eloquent model add the method
public function inGroup($groupName) {
return $this->user_group == $groupName;
}
Edit
if you want to use this middleware in your routes, you can do the following
Route::group(array('before' => 'auth'), function() {
Route::get('/normal/index', ['middleware' => 'group.perm:normal', 'uses' =>'DummyNormalController#index']);
}
But generally it's better to put all your middlewares into your Controller's constructor
public function __construct(){
$this->middleware('group.perm:normal'); // you can also pass in second argument to limit the methods this middleware is applied to : ['only' => ['store', 'update']];
}
And also on this note, Laravel provides built in auth middleware that you can use
public function __construct(){
$this->middleware('auth');
$this->middleware('group.perm:normal');
}
so then your routes would become much cleaner, just:
Route::get('normal/index', 'DummyNormalController#index');
I think the best way to do that is using middlewares. See the doc here
You can easily create a middleware using the following artisan command:
php artisan make:middleware ExtendedMiddleware
If you can't or don't want to use artisan, you need to create a class in The App/Http/Middleware folder.
In this class you'll need the following method to handle the request. In the method you can check for the user group.
public function handle($request, Closure $next)
{
// check user group
if( user_group_ok )
return $next($request); // Continue the request
return redirect('restricted'); // Redidrect
}
You can then use this middleware in your route.php file:
Route::group(['middleware' => 'auth'], function()
{
// Logged-in user with the extended group
Route::group(['middleware' => 'extended'], function()
{
// Restricted routes here
});
// Normal routes here
});
You can create a Middleware called : PermissionFilter
In PermissionFilter, you check if requesting user is in the group or not.
I can't provide a demo for now, but if you want I can make a demo later.
L5 middleware: http://laravel.com/docs/5.1/middleware

Best way to check permissions with sentry 2 at Laravel

Im developing a basic aplication core, first im working with user/groups and & permissions access.
I choose Sentry 2 to work, and i want to limit the access to my.domain/admin to a users or groups that have admin permissions.
Im developing a filter to check if the user is admin and if hasAccess to a specific action, like users.index, or users.custom_fuction.
In my routes i have:
/**
* ADMIN ROUTES
*/
Route::group(array('before' => 'sentry'), function() {
Route::group(array('before' => 'admin'), function() {
Route::group(array('prefix' => 'admin'), function()
{
Route::get('/', function()
{
return View::make('admin');
});
Route::resource('groups', 'GroupController',
array('except' => array('show')));
Route::resource('users', 'UserController',
array('except' => array('show')));
Route::get('users/{id}/groups', 'UserController#groups');
Route::post('users/{id}/groups', 'UserController#store_groups');
Route::get('{table}/{id}/permissions',
'PermissionController#manage_entity');
Route::post('{table}/{id}/permissions',
'PermissionController#update_permissions');
});
});
});
The sentry filter only checks if is loged and rediret to login page, the admin filter is:
/*
* Sentry admin & hasAccess filter
*/
Route::filter('admin', function()
{
$user = Sentry::getUser();
if (!$user->hasAccess('admin')) return Redirect::to('/');
// Ask if user hasAccess to specific action
var_dump(Route::getCurrentRoute()->getPath());
var_dump(Route::getCurrentRoute()->getAction());
});
I have to make another check with the actual route, in the getAction array there are a
'as' => string 'admin.users.index' (length=17)
I can use that for Route::resource i define but, how i did for other functions like groups or permissions.
Maybe there is a better way to handle that, but i dont know it.
Thanks in advice.
I found the solution:
http://laravel.com/docs/routing#named-routes
And now i have:
Route::get('users/{id}/groups', array('as' => 'admin.users.groups', 'uses' => 'UserController#groups'));
Route::post('users/{id}/groups', 'UserController#store_groups');
Route::get('{table}/{id}/permissions', array('as' => 'admin.permissions.manage_entity', 'uses' => 'PermissionController#manage_entity'));
Route::post('{table}/{id}/permissions', 'PermissionController#update_permissions');
And the filters looks like:
Route::filter('admin', function()
{
$user = Sentry::getUser();
$action = Route::getCurrentRoute()->getAction();
if (!$user->hasAccess($action['as'])) return Redirect::to('/admin');
});
But now, all route inside that filter need a as declared or error will popup.
Hope this helps others.

Laravel doesn't change user model on the go

I am using Laravel 4 for my app.
In this app I've got two auth models: Buyers and Users. I don't wont to use User->type field, because this models have absolutely different logic.
Here's my login Controller:
public function postIndex()
{
if (Auth::attempt(array_only(Input::get(), array('email', 'password')), array_only(Input::get(), array('save')))) {
Login::create(array('user_id' => Auth::user()->id, 'session_id' => session_id())); // Создаем новую запись логина вместе с session_id.
return Redirect::to('/');
}
return $this->userauth();
}
public function userauth() {
Config::set('auth.model', 'User');
Config::set('auth.table', 'users');
$test = Config::get('auth.model');
if (Auth::attempt(array_only(Input::get(), array('email', 'password')), array_only(Input::get(), array('save')))) {
Login::create(array('user_id' => Auth::user()->id, 'session_id' => session_id())); // Создаем новую запись логина вместе с session_id.
return Redirect::to('/');
}
Session::flash('error', 'Auth not excepted. '. implode(' ', array_only(Input::get(), array('email', 'password'))));
return Redirect::to('logins')->withInput(Input::except('password'));
}
I've already changed settings in auth.php to work with buyers. When I'm typing login and password for buyer, everything works great. It seems, that after Auth::attempted() it doesn't change settings. It looks like I have to reload Auth object. Can somebody help me?
Buy the way, if I write like this:
public function postIndex()
{
Config::set('auth.model', 'User');
Config::set('auth.table', 'users');
$test = Config::get('auth.model');
if (Auth::attempt(array_only(Input::get(), array('email', 'password')), array_only(Input::get(), array('save')))) {
Login::create(array('user_id' => Auth::user()->id, 'session_id' => session_id())); // Создаем новую запись логина вместе с session_id.
return Redirect::to('/');
}
Session::flash('error', 'Auth not excepted. '. implode(' ', array_only(Input::get(), array('email', 'password'))));
return Redirect::to('logins')->withInput(Input::except('password'));
}
everything works great too.
Short Answer:
You're right. After the first call to Auth::attempt() changes to the config have no effect. During runtime, you have to use Auth::setProvider() to set the model which will be used.
Long Answer:
I don't know how well this will work in your particular setup, but I found your question while trying to do something similar, so I'll show you how I ended up doing it.
In my case, the requirement wasn't simply to have two different types of users. I'm running two websites on separate domains from the same codebase, one for students and one for host families. I have a Student class that will replace User on the one site and Host for the same purpose on the other. Both implement UserInterface and RemindableInterface, so we're good there.
In app/filters.php, I create two special filters:
Route::filter('prep.student', function() {
Auth::setProvider(new Illuminate\Auth\EloquentUserProvider(App::make('hash'), 'Student'));
});
Route::filter('prep.host', function() {
Auth::setProvider(new Illuminate\Auth\EloquentUserProvider(App::make('hash'), 'Host'));
});
Here "Host" and "Student" are the class names you want the Eloquent authentication driver to use.
In app/routes.php, I put my routes into two groups, and on each group I used the proper filter above:
/**
* Routes for Students
*/
Route::group(array('domain' => '[student domain]', 'before' => 'prep.student'), function() {
Route::get('test', function() {
return View::make('student/test');
});
...
});
/**
* Routes for Hosts
*/
Route::group(array('domain' => '[host domain'], 'before' => 'prep.host'), function() {
Route::get('test', function() {
return View::make('host/test');
});
...
});
The result is that the user provider is set with the proper model for each group of routes. You could probably also set the provider in app/start/global.php, but I decided to keep it with routing so that it's very clear that the two groups of routes are for two different domains and have two different authentication models.
I would do this a bit differently were I building it new, but the two websites are the frontend of a large legacy codebase for which I can't significantly alter the database, so I freely admit this is a bit of a hack. If you're not in such a situation, a better solution might be to have a User class for authentication, and then attach that to the two other models you need.

Categories