laravel multiple models in policy - php

I have a two level resource in Laravel as below;
Route::resource("domains", "DomainsController");
Route::resource("domains/{domain}/subdomains", "SubDomainsController");
and I have two policies;
DomainPolicy.php
SubDomainPolicy.php
the problem is that these domains belong to different users, so I have to authorize these domains and subdomains. I can authorize DomainsController easily since all I have to do is Domain::class => DomainPolicy::class in AuthServiceProvider.php.
When it comes to authorizing SubDomainsController I can use the same policy input such as SubDomain::class => SubDomainPolicy::class, BUT when I access the /domains/1/subdomains/create link since there is no Domain::class delivered to the SubDomainPolicy::class it always prevents access to create page.
I use $this->authorizeResource(Domain::class) and $this->authorizeResource(SubDomain::class) in resource controller constructors without any arguments.
I need to pass Domain model to the SubDomainPolicy someway, thanks in advance.

I have found the solution not through a policy but a middleware. Since the models are binded on web.php Domain::class is always delivered to the SubDomainsController class, so I changed the constructor as;
public function __construct(Domain $domain) {
$this->middleware("domain-access");
}
or you can set it on web.php as a middleware group (eg. ['middleware' => 'domain-access']).
In middleware folder create a middleware named DomainAccess.php with this content;
namespace App\Http\Middleware;
use Closure;
use Illuminate\Auth\Access\AuthorizationException;
class DomainAccess
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
*
* #return mixed
*/
public function handle($request, Closure $next)
{
$user = $request->user();
$domain = $request->domain;
if ($domain->user_id != $user->id) {
return redirect("/");
}
return $next($request);
}
}
And, voila! Everything is working perfectly.
Have a beautiful day.

Related

Function AFTER authentication and BEFORE view (laravel)

I'm trying to get settings from the database and put them in the config,
my function need the user id so it can bring his settings only,
in the service provider ( boot function ) there is no authentication yet, can you please advise me to the right place to run my function, please note that I need it to run before the view get rendered because there are settings for the layout inside it, this is my function :
// public static becouse it's inside Class//
public static function getAppSettings(){
if (!config('settings') && Auth::check()) {
$user_id = Auth::user()->id;
$settings = AppSettings::where('user_id', $user_id)->get()->all();
$settings = Cache::remember('settings', 60, function () use ($settings) {
// Laravel >= 5.2, use 'lists' instead of 'pluck' for Laravel <= 5.1
return $settings->pluck('value', 'key')->all();
});
config()->set('settings', $settings);
}else{
// this is for testing//
dd('no');
}
}
without the auth, it can work inside the service provider ( boot function ) but it will bring all settings for all the users.
You can create middleware for this.Middleware calls after routes and before controller
php artisan make:middleware Settings
This will create below class
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class Settings
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
// exicute your logic here
return $next($request);
}
}
You can call your method inside handle and before next
You can read more about this in
https://laravel.com/docs/8.x/middleware

Laravel, first user is only user

I am building a Laravel site for personal use and I would like to make the first user to register on the site be the only user. So with a fresh Laravel install with the ui installed, migration sorted and no users registered, I would like the register route to be reachable. But if there is a registered user, block the register route and only allow the login route to be reachable.
I could do something like this in the web.php
Route:get('/register', function () {...})->auth();
But I would have to do that after I first create a user. I'd rather do it in a more controllable fashion.
Edit
I don't doubt that #yves-kipondo's answer is the more correct option if I were to create this for someone else.
The solution I went with is a simple one. In my register controller I just add a check in the constructor, if there already is a user return a 404.
public function __construct() {
if (!User::all()) {
$this->middleware('guest');
} else {
abort(404);
}
}
You can create a Middleware which will be register on the register route
<?php
namespace App\Http\Middleware;
use Closure;
class RegisterOnce
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (User::count() !== 0) {
// you can redirect wherever you want
return redirect('home');
}
return $next($request);
}
}
After that you can register the middleware in the app/Http/Kernel.php by adding this line after all registered routes middleware like this
protected $routeMiddleware = [
\\ ... previous registered middleware
'once' => App\Http\Middleware\RegisterOnce::class,
];
and you can customize the register route like this in the routes/web.php file
which wille replace the default set by Auth::routes();
Route::get('register', [App\Controllers\Auth\RegisterController::class, 'showRegistrationForm'])
->name('register')
->middleware('once');

Laravel force prefix

In my app I provided prefix e.g. en which my site loads as domain.xyz/en now this is working, but what I need to add is:
force redirect when user visits websites without any prefix:
e.g. user try to load domain.xyz this domain must redirect to domain.xyz/en (default prefix) for the first time in order to site loads, but later if user choose another lang it can be set to that. r.g. domain.xyz/es
Question
What should I write in my middleware to achieve that redirect?
I've tried to use:
1-$request->route().parameters();
and
2-$request->route().getPrefix();
no luck.
code
<?php
namespace App\Http\Middleware;
use Closure;
class PrefixMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
return $next($request);
}
}
Update
Here is how my web.php looks like:
$route_prefix = \Config::get('app.route_prefix');
Route::group(['middleware' => 'verified', 'prefix' => $route_prefix.'/'], function () use ($route_prefix){
Route::get('/', 'HomeController#welcome')->name('homepage');
});
Update 2
Based on comments suggestion now I have something like:
public function handle($request, Closure $next)
{
$prefix = \Config::get('app.route_prefix');
\App::setLocale($prefix);
return $next($request.'/'.$prefix);
}
and it returns:
Call to a member function setUserResolver() on string
Solved
I used this tutorial and it fixed my problem

Use different controller for same route based on user role in Laravel

What I want is I can use different controller for same route based on logged in user's role, so if user logged in with role of admin I want controller for given url is loaded from Admin namespace. I've done like this
Route::group(['middleware'=>['install','auth']],function(){
$role = \Auth::user()->role;
switch ($role)
{
case 'admin':
$prefix = 'Admin\\';
break;
case 'Foo':
$prefix = 'Foo\\';
break;
}
Route::resource('/foo',$prefix.'FooController');
//.......
But is says that Auth::user() is null, is there another approach to do this?
Try auth()->user() or include a specific Auth Module instead of \Auth::user() it might be that the interpreter is using a wrong Auth module since there are quite a few of these.
You can do a redirect using middleware for either the admin or the 'Foo', just check the auth role and return a redirect, to the correct route.
class AdminOnly
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(Auth::user()->role != 'admin'){
return redirect('path/to/non/admin/route');
}
return $next($request);
}
}
Then define both routes in your routes file and use the middleware to redirect between them.

Laravel Middleware Auth for API

I am currently developing and application that has an API which I want to be accessible through middleware that will check if the user is authenticated using either Laravel's default Auth middleware and Tymone's JWT.Auth token based middleware so requests can be authenticated either of the ways.
I can work out how to have one or the other but not both, how could I do this? I'm thinking I need to create a custom middleware that uses these existing middlewares?
I am using Laravel 5.1
Thanks
Turns out I did need to make my own middleware which was easier than I thought:
<?php
namespace App\Http\Middleware;
use Auth;
use JWTAuth;
use Closure;
class APIMiddleware {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next) {
try {
$jwt = JWTAuth::parseToken()->authenticate();
} catch (\Tymon\JWTAuth\Exceptions\JWTException $e) {
$jwt = false;
}
if (Auth::check() || $jwt) {
return $next($request);
} else {
return response('Unauthorized.', 401);
}
}
}
Then I use this middleware on my api route group like so after registering in the kernel:
Route::group(['prefix' => 'api', 'middleware' => ['api.auth']], function() {
I think you can use Route::group in your routes.php file and define the middlewares you want to use in an array.
Route::group(['middleware' => ['auth', 'someOtherMiddleware']], function()
{
Route::get('api/somethinglist', function(){
return App\Something::all();
});
});
If I'm not mistaken all routes defined within that route group is checked against the middleware(s) you specify in the array.

Categories