In Laravel 5.8 app using tymon/jwt-auth 1.0.0
I have users_groups table and I need for logged user for some controller to make check if inside of some group.
For this in routes/api.php I have :
Route::group(['middleware' => 'jwt.auth', 'prefix' => 'manager', 'as' => 'manager.'], function ($router) {
Route::get('users_of_event_selection/{event_id}', 'API\ManagerController#users_of_event_selection');
Route::post('add_user_to_event', 'API\ManagerController#add_user_to_event');
...
I app/Http/Controllers/API/ManagerController.php I added checks:
public function __construct()
{
$this->middleware('jwt.auth', ['except' => []]);
$request = request();
$this->requestData = $request->all();
$loggedUser= Auth::guard('api')->user();
$userGroupsCount = UsersGroups
::getByUserId($loggedUser->id)
->getByGroupId([ACCESS_ROLE_ADMIN,ACCESS_ROLE_MANAGER])
->count();
if($userGroupsCount == 0) {
return response()->json(['error' => 'Unauthorized'], 401);
}
}
But
$userGroupsCount == 0
the code above does not work as I expected and my control's method returns valid data. I suppose I can make
small function and to call it in top on any control's method, but if that ig good way? If jwt-auth has any way to extend
additive checks ?
Thanks!
Related
I have two user types: Operations and Maintenance.
All routes that the Operations user type can be accessed by Maintenance user type but not all routes that Maintenance have can't be accessed by Operations.
Here's my existing code.
Route::group(['middleware'=>'maintenance'], function(){
//routes here
Route::group(['middleware'=>'operations'], function(){
//routes here
});
});
kernel.php
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'revalidate' => \App\Http\Middleware\RevalidateBackHistory::class,
'dev' => \App\Http\Middleware\isDev::class,
'operations' => \App\Http\Middleware\operations::class,
'maintenance' => \App\Http\Middleware\maintenance::class,
];
middleware/operations.php
public function handle($request, Closure $next)
{
$a = UserAccess::where(['employee_id'=>\Auth::user()->id, 'user_type_id'=>1])->first();
if($a){
return $next($request);
}
return redirect('/');
}
But it doesn't work. Maintenance can access all it's routes but doesn't have access to operations and Operations doesn't have access to any route at all.
NOTE: Both groups are inside the auth middleware group
Found a work around for this. I created another middleware that caters both Operations and Maintenance by passing two parameters to that middleware specifically 1 = Operations and 2 = Maintenance and had the middleware check for access. Here's the code:
Route::group(['middleware'=>'access:2,2'], function(){
//Routes that can be accessed by Maintenance user type
});
Route::group(['middleware'=>['access:1,2']], function(){
//Routes that can be accesed by Operations and Maintenance user type.
});
Here's the middleware:
public function handle($request, Closure $next, $ops, $main)
{
$a = UserAccess::where('employee_id',\Auth::user()->id)->whereIn('user_type_id', [$ops, $main])->first();
if($a){
return $next($request);
}
return redirect('/');
}
EDIT:
Optimized code to remove parameter redundancy.
Web.php:
Route::group(['middleware'=>'access:2'], function(){
//Routes that can be accessed by Maintenance user type
});
Route::group(['middleware'=>['access:1,2']], function(){
//Routes that can be accesed by Operations and Maintenance user type.
});
access.php
public function handle($request, Closure $next, $ops, $main = 0)
{
$a = UserAccess::where('employee_id',\Auth::user()->id)->whereIn('user_type_id', [$ops, $main])->first();
if($a){
return $next($request);
}
return redirect('/');
}
I think you've missed an additional array inside of your where query, try changing the code inside of your operations middleware like so:
$a = UserAccess::where([
['employee_id', \Auth::user()->id],
['user_type_id', 1]
])->first();
EDIT:
Then try moving both of your middlewares into $middlewareGroups below web middleware, like this:
'web' => [
...
],
'operations' => \App\Http\Middleware\operations::class,
'maintenance' => \App\Http\Middleware\maintenance::class,
EDIT 2:
Change the code for $a variable to this
$a = UserAccess::where([
['employee_id', Auth::user()->id], //Hence there is no \ before Auth
['user_type_id', 1]
])->first();
Then at the top of that same file include the following use:
use Illuminate\Support\Facades\Auth;
EDIT 3:
You can add additional parameters to the request which are then sent forward, like this:
Middlewares/operations.php
function handle($request, Closure $next) {
...
$request->attributes->add(['passedOperations' => true]);
return next($next);
}
Middlewares/maintenance.php
function handle($request, Closure $next) {
...
if($request->get('passedOperations')) {
return next($next);
} else {
//didn't pass operations
}
}
I want to make a middleware to protect admin routes in laravel 5.2 app.
user_type isn't a field in users table in my db, but in a separate table :
Admin's user_type_id is 4
I made this middleware :
class AdminMiddleware
{
public function handle($request, Closure $next)
{
$authedUserID = Auth::id();
$user = User::query()
->leftjoin('users_user_types as uut', 'uut.user_id', '=', 'users.id')
->where('uut.user_id',"=","$authedUserID")
->get(['users.*',
'uut.user_type_id as userType'])->first();
if ($user['userType'] !=4)
{
return redirect('/home');
}
return $next($request);
}
}
and put this in $routeMiddleware array in kernel.php :
'admin' => \App\Http\Middleware\AdminMiddleware::class
and this is one of admin routes I want to apply my middleware on :
Route::get('ptyadmin', 'AdminController#show')->middleware('admin');
The Problem is that when I go to this route with admin user(user_type = 4) or normal user(user_type = 1) it redirects to /home in both cases !!
Why does this happen and how can I fix it ??
first method returns object
if ($user->userType !=4)
Also use selectinstead get
->select(['users.*','uut.user_type_id as userType'])->first();
It works when I put jwt.auth middleware in routes.php :
Route::get('ptyadmin', 'AdminController#show')
->middleware('jwt.auth')
->middleware('admin');
instead of putting it in AdminController :
public function __construct(User $user, JWTAuth $jwtauth)
{
$this->middleware('jwt.auth', ['except' => []]);
}
I have a controller in Laravel 5.
I would like to write a controller function that accepts variable arguments.
For example,
public function show(Request $request, ...$id)
{
// handle multiple $id values here
}
The reason is that I have a url structure that has 'nested' models.
For instance:
\item\{$id}
\parent\{$parent_id}\item\{$id}
\grandparent\{$grandparent_id}\parent\{$parent_id}\item\{$id}
The routes are defined as:
Route::resource('item', 'ItemController');
Route::resource('parent.item', 'ParentController');
Route::resource('grandparent.parent.item', 'GrandparentController');
My desire is to write a single show() method as a trait that each controller can use.
Because of the structure of my database, it is possible.
But the UrlGenerator keeps throwing a UrlGenerationException when I try to use variable arguments. It seems like it doesn't understand this construct?
Ok, here's an idea for you that should get you on the right path:
For the various resource routes you defined, re-declare them to exclude the 'show' action, and define a separate GET route to map the routes you are trying to centralise.
app/Http/routes.php:
Route::resource('item', 'ItemController', ['except' => ['show']]);
Route::get('item/{item}', ['uses' => 'AggregateController#handleShow', 'as' => 'item.show']);
Route::resource('parent.item', 'ParentController', ['except' => ['show']]);
Route::get('parent/{parent}/item/{item}', ['uses' => 'AggregateController#handleShow', 'as' => 'parent.item.show']);
Route::resource('grandparent.parent.item', 'GrandParentController', ['except' => ['show']]);
Route::get('grandparent/{grandparent}/parent/{parent}/item/{item}', ['uses' => 'AggregateController#handleShow', 'as' => 'grandparent.parent.item.show']);
app/Http/Controllers/AggregateController.php:
class AggregateController extends Controller
{
public function handleShow(Request $request, ...$items)
{
dd($request->path(), $items);
}
}
http://stackoverflow42005960.dev/grandparent/1/parent/2/item/3:
"grandparent/1/parent/2/item/3"
array:3 [▼
0 => "1"
1 => "2"
2 => "3"
]
If you still have issues with getting the variable arguments, then check your PHP version and if < 5.6 you'll have to use func_get_args()
There're many ways to go about this. For example, you can use a comma separated list in routes and simply explode in controller.
The way you have it currently, you will have to use a fixed number of optional parameters, e.g.
public function show(Request $request, $id1, $id2 = false, $id3 = false)
{
//if parent item exists
if($id2)
{
//if grandparent item resource
if($id3)
{
}
}
else
{
//just item
}
}
I am using Hashid to hide the id of a resource in Laravel 5.
Here is the route bind in the routes file:
Route::bind('schedule', function($value, $route)
{
$hashids = new Hashids\Hashids(env('APP_KEY'),8);
if( isset($hashids->decode($value)[0]) )
{
$id = $hashids->decode($value)[0];
return App\Schedule::findOrFail($id);
}
App::abort(404);
});
And in the model:
public function getRouteKey()
{
$hashids = new \Hashids\Hashids(env('APP_KEY'),8);
return $hashids->encode($this->getKey());
}
Now this works fine the resource displays perfectly and the ID is hashed.
BUT when I go to my create route, it 404's - if I remove App::abort(404) the create route goes to the resource 'show' view without any data...
Here is the Create route:
Route::get('schedules/create', [
'uses' => 'SchedulesController#create',
'as' => 'schedules.create'
]);
The Show route:
Route::get('schedules/{schedule}', [
'uses' => 'Schedules Controller#show',
'as' => 'schedules.show'
]);
I am also binding the model to the route:
Route::model('schedule', 'App\Schedule');
Any ideas why my create view is not showing correctly? The index view displays fine.
Turns out to solve this, I had to rearrange my crud routes.
Create needed to come before the Show route...
There's a package that does exactly what you want to do: https://github.com/balping/laravel-hashslug
Also note, that it's not a good idea to use APP_KEY as salt because it can be exposed.
Using the above package all you need to do is add a trait and typehint in controller:
class Post extends Model {
use HasHashSlug;
}
// routes/web.php
Route::resource('/posts', 'PostController');
// app/Http/Controllers/PostController.php
public function show(Post $post){
return view('post.show', compact('post'));
}
I've got a route:
Route::get('/', array('before' => 'auth.basic', function() {
return Response::json(
array(
'name' => 'John Smith',
'age' => 42
)
);
}));
What I want is to add where active= 1 to the authentication query.
I know that I can add extra conditions to Auth::attempt() but I don't know how to do this using Auth::basic().
There's two ways to do this.
1. Modify the filter and use Auth::attempt()
That's the simple way. Head over to app/filter.php and change the auth.basic entry to this
Route::filter('auth.basic', function()
{
if(Auth::check()) return; // already logged in
if(Auth::attempt(array(
'email' => Request::getUser(),
'password' => Request::getPassword(),
'active' => 1))){
return;
}
$headers = array('WWW-Authenticate' => 'Basic');
return Response::make('Invalid credentials.', 401, $headers);
});
That's basically what the Laravel Guard class does when Auth::basic() gets called (except for the 'active' part obviously)
2. Extend Auth
Now that's the "more elegant" way to do it. Although I don't know if its really worth it in your case. I'm also not going to describe it in every detail.
To extend Laravel Auth you can use Auth::extend(). Here's an example:
Auth::extend('my_driver', function() {
$model = Config::get('auth.model');
$provider = new Illuminate\Auth\EloquentUserProvider(App::make('hash'), $model);
return new MyGuard($provider, App::make('session.store'));
});
The MyGuard class would extend Illuminate\Auth\Guard and override the getBasicCredentials() method. Finally in config/auth.php set 'driver' => 'my_driver'
If you choose the second method and need more help, write a comment...
How I did it (Laravel 5.8):
Methods basic and basicOnce from SessionGuard accepts two arguments: $field where you can set username column and $extraConditions where you can set, well, extra conditions.
You can extend AuthenticateWithBasicAuth and override handle method to get something like
class CustomBasicAuth extends AuthenticateWithBasicAuth
public function handle($request, Closure $next, $guard = null, $field = null)
{
$this->auth->guard($guard)->basic('email', ['active' => 1]);
return $next($request);
}
and don't forget to register and use new middleware.