How can I create dynamic routes? - php

I have this path:
http://localhost:8000/home
I want when a regular user opens path above, then I call this controller:
mainPage#index
But when a admin opens that path, then I call this controller:
panelPage#index
So as you see, I'm looking for "dynamic routes" kinda .. Is implementing that possible? In other word, can I call two different controllers for admin and regular member ?

This is a good case to use Middlewares to filter HTTP requests.
You could also do something conditional in your routes file, like:
if (Auth::user()->isAdmin()){
Route::get('/', 'panelPage#index');
}
else {
Route::get('/', 'mainPage#index');
}
Depending on what your application looks like, you can define isAdmin() in your User model. This is a very simple example where you have a column called role_id and id nr 1 equals admin. If authenticated user is admin, it displays true, otherwise false:
public function isAdmin()
{
return Auth::user()->role_id == 1;
}
A more dynamic and advanced approach would be to create a role table, and associate the role with the user with a role_user pivot table.
If you want it to take a step further, you can create a permissions table and associate the role with the permissions with a permission_role pivot table. Then you can in your application define that a permission is needed to be able to do an action and add all the permissions that a given user role has in that pivot table. Then you just check if the user (with a specific role) has the given permissions.

For best practice, you could use Middleware to sort-out and categorize your routes and controllers.
In this - relatively simple - case, you could also use something like this (in your routes file):
if(!is_null(Auth::user())) {
// first check if user is logged in, else Auth::user() will return null
$uses = 'mainPage#index';
if(Auth::user()->admin) {
$uses = 'panelPage#index';
}
Route::get('/', $uses);
}
Update
Or you could wrap everything inside this if statement in an auth middleware group, like this:
Route::group(['middleware' => ['auth']], function(){
$uses = 'mainPage#index';
if(Auth::user()->admin) {
$uses = 'panelPage#index';
}
Route::get('/', $uses);
});
Also make sure that your users table has a column named 'admin'.

Related

how to block Url to all user except super admin

it should forbid that the user grabs this url:
?main_title=banner
?main_title=law
?main_title=faq
with this
if(\Auth::user()->hasRole(['super_admin']))
I am going to assume that you are using spatie/laravel-permission based on your example code.
Laravel Permission comes with built-in role middlewares
One of the ways you could use them is by grouping the routes you want to be accessible only by super admins
Route::group(['middleware' => ['role:super_admin']], function () {
// YOUR ROUTES HERE
});
It's always good to using the middlewares ,
So in your case first create a Trait for roles
public function isSuperadmin(){
return Auth::user()->role->role=='superadmin';
}
After that create a middlewar like superadmin for the superuser and in that first include your trait
use App\Traits\Roles;
after that
use Roles;
public function handle($request, Closure $next)
{
if(!$this->isSuperadmin())
{
return back();
}
return $next($request);
}
and just register the middleware in the app/http/kernal.php in protected $routeMiddleware function
'superadmin' => \App\Http\Middleware\superadmin::class,
so it's make your life very easy now you don't need to check the url or role every time , for any url you want to block for other users just use
Route::get('/?main_title=law', 'HomeController#function')->middleware('superadmin')->name('admin-dashboard-home');
so if the user role is superadmin then he is allow to assess the url you can redirect the other users or show the error message :)

laravel - routes with same url and with diffrent middlewares and diffrents controllers override each others

so i have my routes defined within a global route group and inside i have two others groups, on with middleware 'employee' and one with middleware 'admin' and each one have their own controllers.
The two groups have a route index with the same url " /document " but each executes a different controller.
My problem is the index route that the last defined group always overrides the previous one.
The wanted behaviour is that if employee middleware is valid, execute the empController#index action, else if the admin controller is valid, execute the adminController#index action while keeping the same url for both, and i must keep each route in a seperate group, and the two routes must have the same url.
How can i acheive that? Thanks
here are the two conflicting routes
Route::group(['as' => 'admin.', 'middleware' => ['admin']], function() { Route::get('/document', 'Admin\DocumentController#index')->name('document.index'); .... });
Route::group(['middleware' => ['employee']], function() { Route::get('/document', 'DocumentController#index')->name('document.index'); .... });
It sounds like you want to perform different actions based on whether the request is coming from an employee or an admin. My suggestion would be to have one url, one route and one controller function for this.
Within the controller function (or in middleware that attaches its findings to the request if you wanted to do that), have logic to call one method or another.
A pseudo example in PersonController.php:
if(\Auth::user()->isAdmin()) {
// return an admin view or perform an admin function
else {
// return an employee view or perform an employee function
}
Where the isAdmin() function is something you've defined in a model like User.php.

Laravel Multiple Middleware in Route with OR Condition

I wonder if I can do this in Laravel Route. Let's say I have Admin, Premium and User (which can be login too by using Auth) Middleware. Also, I have controller with methods like this: index, create, edit, delete and I want Admin to be able do all those things, but Premium can only be able to access index method, and User can't access anything in this controller (he can access another controller). I know I can use except or only middleware method like this:
public function __construct()
{
$this->middleware('premium')->only('index');
$this->middleware('admin');
// or maybe $this->middleware('admin')->except('index');
}
but when I try to put these two middlewares in __construct method they will start to conflict each other, it makes sense because index method can be access by Premium but then can't be access by the Admin itself. By the way, my middleware is simply checking:
if (Auth::check()) {
if (Auth::user()->role == 'Admin') {
return $next($request);
}
}
return redirect('/home');
So, back to my question, can I have OR Middleware so I can avoid conflict from multiple middleware (which is must be AND condition when they written at the same controller constructor)?
If you change up the way your logic is thinking a little bit, the answer becomes pretty easy. You can create new middleware that checks if it can access the specific method.
So create the following middleware 'CanAccessIndex':
if (Auth::check()) {
if (Auth::user()->role == 'Admin' || Auth::user()->role == 'Premium') {
return $next($request);
}
}
return redirect('/home');
Then, you can put that middleware on the index function (instead of the premium middleware) and put your admin middleware on everything EXCEPT index. Like so:
public function __construct()
{
$this->middleware('canAccessIndex')->only('index');
$this->middleware('admin')->except('index');
}
That's one way to do it.
You need middleware group for this, and to manage these hierarchy of access layer, you can simply use Route grouping.
I will demo an example of what I mean:
Say you have auth middleware general for authenticated users (i.e everybody), then another called premium for premium member, and admin for the Admin.
Then you'll group based on the access level:
Route::middleware('auth')->group(function(){
Route::middleware('premium')->group(function(){
Route::post('/create', 'HomeController#create')->middleware('admin');
Route::put('/update/{id}', 'HomeController#update')->middleware('admin');
Route::get('/index', 'HomeController#index');
Route::put('/delete/{id}', 'HomeController#delete')->middleware('admin');
});
});
So you can have general check in your middleware with a check. It would have been much easier if you have role level say 3 for admin and 2 for premium member. So we can have for the premium middleware:
public function handle($request, Closure $next)
{
return auth()->user->role >= 2
? $next($request)
: redirect('/home');
}
This is just an example. You can do further check based on your need but more importantly, ensure your admin middleware to checks the exact role level that is allowed.

Laravel 5.1 ACL route resource not working

After following a tutorial on how the built-in acl of laravel works I tried it and it works well by defining every route by itself.
Now I'm trying to use a resource but it's not working as intended. I added the following code to my routes file:
Route::group(['middleware' => 'acl:create_client'], function()
{
Route::resource('clients', 'ClientController');
});
Now I understand what the problem is:
all the methods in the Clientcontroller will be checked against my db if this user has the acl:create_client, resulting in all methods available to the logged in user that has this acl.
How do I split every method to use it's own acl without having to write it like this:
Route::get('/client/create', [
'middleware' => 'acl:create_client',
'as' => 'clients.create',
'uses' => 'ClientController#create'
]);
Resulting in something like this:
create needs create_client
index needs index_client
update need update_client
etc etc
The bottom line is: you need to setup the 'list' in the Access Control List (ACL) somehow. IMO, the most flexible way would be to pull this list from the database based on the session user; you're off to a good start. You can skip the explicit route assignment by using the already assigned 'as' that you define in a route. An example route:
Route::get('/', ['as'=>'clients.create', 'uses'=>'ClientsController#create']);
Here, you would use the 'clients.create' in your ACL check. Just remember: the ACL will still need the 'as' value set for all your routes (which is good to do anyway).
Step-by-step
Now that you have the required background information, here's how to get it working. These steps assume you were able to correctly setup the tutorial code and database. This will stick to the original tutorial setup and will focus on making the ACL independent from the route configuration.
1) In App\Http\Middleware\Acl\CheckPermission, you will need to replace the argument $permission = null with the 'as' string that you set in routes.php. The new code:
<?php namespace App\Http\Middleware;
use Closure;
class CheckPermission
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next/*, $permission = null REMOVE THIS*/)
{
// Add the next two lines:
$action = $request->route()->getAction();
$permission = isset($action['as']) ? $action['as'] : '';
if (!app('Illuminate\Contracts\Auth\Guard')->guest()) {
if ($request->user()->can($permission)) {
return $next($request);
}
}
return $request->ajax ? response('Unauthorized.', 401) : redirect('/login');
}
}
2) Now, you need to assign this middleware in a different manner. You do not want to use a specific permission, but instead use the 'as' string we just setup in the middleware. You can assign the middleware in two different ways: a) assign it to a group of routes, or b) assign it to every page. I suggest using 2a instead of 2b, because you may not want to use the ACL on all routes.
2a) Here's the method to assign it to only a group of routes. The two important things to notice here are the 'as'=>'clients.*' strings and the assignment of the middleware to the route group 'middleware' => 'acl'. Also note this route group does not pass the extra string parameter like the tutorial does (e.g. 'middleware' => 'acl:manage_user'). This is because we removed that argument from the handle() function above. You will need to change these example routes to match your target URIs and controller functions.
Route::group(['middleware' => 'acl'], function()
{
Route::get('/clients', ['as'=>'clients.view', 'uses'=>'ClientsController#index']);
Route::get('/clients/new', ['as'=>'clients.create', 'uses'=>'ClientsController#create']);
// Add more routes ...
}
2b) Here's how to assign it to every page. The tutorial uses the file /app/Http/Kernel.php to setup the middleware as a $routeMiddleware. This is the correct way to do it for step 2a above, but not if you want it on every page. To make the middleware a global middleware: add '\App\Http\Middleware\CheckPermission' to the $middleware variable found in the same file. You will not need the $routeMiddleware addition from the tutorial if you use the global variable.
3) In the tutorial database, you need to use the 'as' string in the permissions table within the permission_slug column. Here are example SQL inserts that allows user with id 123 to access route clients.create. These two create the permission and role we need to create access to the 'client.create' route.
INSERT INTO permissions ('permission_title', 'permission_slug', 'permission_description')
VALUES ('Create a Client', 'clients.create', 'Allow the user to create a client');
INSERT INTO roles ('role_title', 'role_slug')
VALUES ('Client Admin', 'clients.admin');
For the next query, you need to know the id of the two rows above. This assumes your database was newly created with no rows yet added, so each of the inserts will be id=1. This says: permission with id=1 is assigned to role with id=1.
INSERT INTO permission_role ('permission_id', 'role_id') VALUES (1, 1);
The next query also assumes the new role will be id=1 and the user id is 123. This assigns the new role with id=1 to the existing user with id=123.
INSERT INTO role_user ('role_id', 'user_id') VALUES (1, 123);
At this point, you should have a user with id=123 that has the Client Admin role. The Client Admin role should have the 'clients.create' permission. When you are logged in as user id=123, you will be verified to have the 'clients.create' permission, and you should be able to access the page (example.com/clients/new in my example). Any other user will not have access, and they will be redirected to the login page (this doesn't make sense to me if you're already logged in; this is just what the tutorial setup).
I recommend you do not build acl by yourself,there have some good packages out there like entrust
and if you truly want know the principle or laravel acl just follow this video tutorial from laracast laracast laravel acl tutorial

How can I set a specific method of aLaravel 4 resource route in my ACL

I have a fairly simple ACL system set up. The filter checks if the user is part of a user group which has access to the route or if the user has access to the route. It works for individual routes and works for resources in general. However I want some users to have access to a specific method of a resource route, but not all of it. for example, user1 is part of the admin group and always has access to the admin resource route but user2 is not a part of the admin user group and I want to give him access to teh resource admin/create. How can I go about this in Laravel 4 with my setup
Database
Routes:
id
route
created_by
last_editted_by
created
updated
deleted_at
acl (table it looks at to see if user has access)
id
routes_id
user_id
group_id
created
updated
deleted_at
Filter
if (Auth::check()){
$route = Request::segment(1);
$user_id = Auth::user()->id;
$acl_count = Acls::join('routes','routes.id','=','acl.routes_id')
->where('routes.route','=',$route)
->Where(function($in_parenthesis) use($user_id){
$in_parenthesis->whereIn('acl.group_id',function($where_in) use($user_id){
$where_in->select('group_id')
->from('user_group_junction')
->where('user_id','=',$user_id);
})
->orWhere('acl.user_id','=',$user_id);
})
->count();
if($acl_count < 1){
return Redirect::to('/');
}
}else{
return Redirect::to('/');
}
Routes
Route::get('/','HomeController#index');
Route::get('login','AuthorizationController#loginForm');
Route::post('authenticate','HomeController#authenticate');
Route::get('logout','HomeController#logout');
Route::group(array('before'=>'auth'),function(){
Route::group(array('before'=>'user_permission'),function(){
Route::get('protected','HomeController#protectedPage');
Route::resource('sources', 'SourcesController');
Route::resource('admins', 'AdminsController');
});
});
You can use beforeFilter inside the __construct method of the AdminBaseController like this (create a different one for admin controllers only)
class AdminController extends AdminBaseController {
function __construct() {
// Use filter on all methods but not on create
$this->beforeFilter('admin', array('except' => array('create')));
}
}
Also, you may directly use this beforeFilter inside your resource controller and use except or you can use only (only works reverse of except, allows access to all but filters only mentioned methods in the array). You can also check conditions inside the constructor method s well.
I figured out that part of my problem is with the filter. I am only looking at the first segment of the url which doesn't work correctly if my route (whether it is a resource or just a route with a "/" in it) won't work. Thus I asked another question located here

Categories