How to bypass Exception Handling for specific routes in Laravel? - laravel

I am trying to build an installer such as if laravel catch PDOException of 1049, then it will redirect to a route that will enable the user to input database credentials. Later, I will write those to .env & run artisan command. But stuck at too many redirect error. Also I have a separate route file for this route with no middleware declared.
if ($exception instanceof \PDOException) {
$error_code = $exception->getCode();
if($error_code == 2002 || $error_code == 1049){
return response()->redirectToRoute('installer');
}
}

Got the issue solved. Actually the problem was I had modified the kernel.php and brought
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
to protected $middleware from protected $middlewareGroup => web for session message handling with toastr. Just reversed them as it was and everything works fine.
2nd Solution
Just look at your .env SESSION_DRIVER value. Set it to file. Also you can update 'driver' => env('SESSION_DRIVER', 'file') in your config/session.php for safety. By default, it's the default value. But in which application, I was working, someone changed it to database. That's why I was facing such issues.

Related

How to disable csrf protection for a route with dynamic parameter?

I have a route which has a dynamic parameter at the end of the URL. In this route, I fetch data which is sent from an external API with the post method. As 419 page expired error occurs when the external API sends post request, I need to disable csrf protection for this route.
Related route:
Route::group(['middleware' => ['auth:student']], function (){
Route::post('Result', 'ExamController#Result')->name('exam.Result');
}
My URL example:
http://localhost.dev/student/Result?Id=N7utfGkwOLebxMWGA5iUC4S23jgRzW
I tried to add this code in VerifyCsrfToken file at App\Http\Middleware:
protected $except = [
'student/Result/*',
];
It doesn't work. But when I try student/*, it works perfectly. However, disabling the csrf protection for all student path is not what I want.
I tried also this way by getting reference on this thread:
Route::post('Result', [
'uses' => 'ExamController#Result',
'nocsrf' => 'true'
])->name('exam.Result');
That didn't work either.
How can I disable csrf protection in this scenario?
You made a typo at App\Http\Middleware, instead of:
protected $except = [
'student/Result/*',
];
You need to use:
protected $except = [
'student/Result',
];
Also, based on documentation you can specify the full url that need to be excepted:
protected $except = [
'http://localhost.dev/student/Result',
];
Be aware, that you don't need to add parameters part (everything after ? sign, e.g. ?Id=N7utfGkwOLebxMWGA5iUC4S23jgRzW) of route here.
Try this one (remove the slash and asterisk):
protected $except = [
'student/Result',
];

Is there an tutorial for how to set up an ldap server and after to connect it with a simple laravel app?

I set up the ldap using DigitalOcean tutorial and installed AdLdap2 package and if anybody can clarify to me why on login to the ldap server still try to login with default username and password, i can't explain properly the problem if it doesn't make sense to you, can you just put here some links which helped you to set up the server and connect a laravel project with ldap
I know I'm a little late but these are my notes from when I set it up for one of my apps.
Adldap2-Laravel (NoDatabaseProvider)
Installation
Install the Adldap2-laravel package:
$ composer require adldap2/adldap2-laravel
Create the auth scaffolding:
$ php artisan make:auth
Configuration
Define the following environment variables in your .env file:
# .env
LDAP_HOSTS=[192.168.1.10]
LDAP_PORT=[389]
LDAP_BASE_DN=[DC=contoso,DC=com]
LDAP_USERNAME=[ldap#contoso.com]
LDAP_PASSWORD=[password123]
Disable the default routes if only using LDAP authentication. In your /routes/web.php file, make the following changes for Auth::routes() :
# /routes/web.php
Auth::routes([
'reset' => false,
'verify' => false,
'register' => false,
])
In your /resources/views/auth/login.blade.php file, change the label for email address to Username, remove the #error('email) section from the corresponding text input and change the name on that text input to whatever LDAP attribute you are looking users up by (samaccountname or userprincipalname) Finished text input should look something similar to this:
# /resources/views/auth/login.blade.php
<input type="text" class="form-control" name="samaccountname" value="{{ old('samaccountname') }}" required autofocus>
Create the ldap.php and ldap_auth.php file by running the following two commands:
$ php artisan vendor:publish --provider "Adldap\Laravel\AdldapServiceProvider"
$ php artisan vendor:publish --provider "Adldap\Laravel\AdldapAuthServiceProvider"
Change the user driver to use ldap instead of eloquent under providers in /config/auth.php and comment out the model below it, like so:
# /config/auth.php
'providers' => [
'users' => [
'driver' => 'ldap', // <- was eloquent, changed to ldap
# 'model' => App\User::class, // <- commented out, not using anymore
In /config/ldap_auth.php:
Change the provider from DatabaseUserProvider to NoDatabaseUserProvider:
# /config/ldap_auth.php
'provider' => Adldap\Laravel\Auth\NoDatabaseProvider::class
Change the locate_users_by under ldap to whatever field you plan to authenticate users by (samaccountname or userprincipalname)
# /config/ldap_auth.php
'ldap' => [
'locate_users_by' => 'samaccountname',
'bind_users_by' => 'distinguishedname',
],
In your /app/Http/Controllers/Auth/LoginController.php file, you must add a username() function that returns the LDAP attribute you are authenticating users by:
# /app/Http/Controllers/Auth/LoginController.php
public function username()
{
return 'samaccountname';
}
In your /config/app.php file, it's helpful to add the alias for the Adldap Facade to keep from having to type it out every time you need to access it:
# /config/app.php
'aliases' => [
'App' => Illuminate\Support\Facades\App::class,
'Arr' => Illuminate\Support\Arr::class,
. // scroll
. // to
. // the
. // bottom
'View' => Illuminate\Support\Facades\View::class,
'Adldap' => Adldap\Laravel\Facades\Adldap::class // <- add this
],
Usage
After successfully authenticating, if you add use Auth; in your controller with the other use contracts in the beginning of the file, you can access the authenticated user's attributes like so:
# /app/Http/Controllers/HomeController
use Auth;
public function index()
{
$user = Auth::user();
$cn = $user->getCommonName(); // assigns currently authenticated user's Common Name
return view('home')->with('cn', $cn);
// using {{ $cn }} on the home's view would output the Common Name
}
If you want to query the LDAP server for all other users, you can add use Adldap; with the other use contracts and use the query builder:
# /app/Http/Controllers/HomeController
use Adldap;
public function index()
{
$users = Adldap::search()->where('objectclass', '=', 'person')->sortBy('cn', 'asc')->get();
return view('home', compact('users'))
// use a foreach loop on the view to iterate through users and attributes
}

How to prevent an error on undefined routes in Laravel 5.5

I developed an API with Laravel 5.5. All is working fine.
But imagine that a user enter an "api" url directly in the browser (for example: api/books), then they will receive an error:
InvalidArgumentException
Route [login] not defined.
How to prevent this? I tried to add some routes in the routes/web.php file, but without success.
Or perhaps I should do nothing (there will be very few users who will do that)?
I found the answer here:
Laravel 5.5 change unauthenticated login redirect url
I only do that in the "app/Exceptions/Handler.php" file, I modified the function "render" like that :
public function render($request, Exception $exception)
{
// return parent::render($request, $exception);
// add dom
return redirect('/');
// or redirection with a json
/*
return response()->json(
[
'errors' => [
'status' => 401,
'message' => 'Unauthenticated',
]
], 401
);
*/
}
And it works fine. As the "Laravel" part will be used only as back end for APIs, it will be enough.

Migration to Laravel, Vanity URLs, middleware for all requests, Auth::user and $errors issue

I created a middleware class named PathParser which runs on every request. The purpose is to handle requests for "vanity URL paths" that we allowed users to create in our pre-Laravel vanilla PHP app. For example: A user created a URL path such as: http://example.com/i-love-this-place
What PathParser does is to check for 404 responses, and then look to see if the URL path matched one of our old vanity paths. Like this:
class PathParser
{
public function handle($request, Closure $next, $guard = null)
{
$next_response = $next($request);
$status_code = $next_response->getStatusCode();
if ($status_code === 404) {
$script_url = $request->server("SCRIPT_URL");
$vanity_controller = new VanityController();
$found_vanity_id = Search::findVanityPath($script_url);
if (!empty($found_vanity_id)) {
$next_response = response()->make($vanity_controller->one($found_vanity_id));
}
}
return $next_response;
}
}
Assume the following:
A user has never created a URL path that would conflict with any route
I must support a number of existing (pre-Laravel) vanity URL paths that are out in the wild -- posted to social media and so on.
In Kernel.php, I have the following:
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\App\Http\Middleware\PathParser::class,
//\Illuminate\Session\Middleware\StartSession::class,
//\Illuminate\View\Middleware\ShareErrorsFromSession::class,
];
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
In the $middleware array I tried added StartSession and ShareErrorsFromSession (uncommenting the 2 lines above), and this works partially. But with two major problems:
Auth::user is null, even for requests made to vanity paths by logged in users
$errors no longer gets populated on form submission (such as on the registration and login pages) when a user submits incorrect/invalid information
Is there a way both to check the route on all requests and get ahold of the authenticated user, also preserving $errors?
I have a feeling I don't understand the request lifecycle well enough to succeed. But maybe there is a way?
If it's not possible to do what I require, then use of 302 redirection to a standardized prefixed path (such as http://example.com/vanity/i-love-this-place) is a fine solution. But I'm hoping there is another means.
A couple suggestions:
If you don't need auth/sessions/etc, then you can just handle the Symfony\Component\HttpKernel\Exception\NotFoundHttpException exception inside your application's exception handler.
In app/Exceptions/Handler.php, modify the render() method to look something like:
public function render($request, Exception $e)
{
if ($e instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException) {
// your code that returns a Response
}
return parent::render($request, Exception $e);
}
If you do need auth/sessions/etc, I would suggest creating a "catchall" route at the end of your routes file. For example, as the very last line in your routes/web.php file, put:
Route::any('{catchall}', 'VanityController#handle')->where('catchall', '(.*)');
Then, inside your VanityController, have a handle method that looks like:
public function handle(Request $request, $url)
{
// logic to search and render your vanity page for $url
// if no vanity page was found:
throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException();
}

Accessing Route URL Parameter in Middleware in Laravel 5.3

I am having a hard time accessing the Route URL Parameter in Middleware after updating from Laravel 5.1 to Laravel 5.3.
Here is my route file:
Route::group(['middleware' => ['app.access']], function()
{
Route::resource('apps/{apps}/houses', 'HouseController',
['except' => ['index', 'create', 'edit']]);
Route::get('apps/{apps}/houses/colour/{colour}', 'HouseController#colourCheck');
...
}
Kernel.php has RouteMiddleware updated like this:
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,
// Added ones....
'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class,
'jwt.refresh' => \Tymon\JWTAuth\Middleware\RefreshToken::class,
// Custom Middleware
'app.access' => \App\Http\Middleware\AppAccess::class,
];
In Laravel 5.1 and I was able to access route parameter from middleware like this and I had no trouble:
public function handle($request, Closure $next)
{
$appId = $request->route('apps');
...
}
But, since upgrading to Laravel 5.3, the above code started returned $appId as null. I also tried accessing route parameter like $appId = $request->apps and this was not working as well.
So I then changed the Middleware to get the url parameter like this:
public function handle($request, Closure $next)
{
$appId = $request->route()->parameters('apps');
...
}
Since changing to the above, the middleware seems to be working for the first route. But then, I am having problem when going to the second route that has 2 parameters. For some reason, the above code fails and when returning $request->route()->parameters('apps') from middleware, I am getting an array like this:
Array
(
[apps] => 1
[colour] => green
)
Why is that? Why is it not returning just the $appId which is 1? Am I missing something or is it a bug in Laravel 5.3? I want the Middleware to access only the appId parameter from the url and nothing else from the route. Can someone help me here please?
Update 1:
I tried changing the Middleware to get parameter like this:
$parameters = $request->route()->parameters();
$appId = $parameters['apps'];
In the above, I am getting the error:
Undefined index: apps
But when I print_r($parameters);, the output is like this:
Array
(
[apps] => 1
[day] => tuesday
)
[]
Solution:
In the end, I found out the reason for this odd behaviour was due the parameter name in the url.
The following resources routes were generating route url parameter as app instead of apps. I dont know why though:
Route::resource('apps/{apps}/houses', 'HouseController',
['except' => ['index', 'create', 'edit']]);
Whereas the following routes were generating route url parameter as apps:
Route::get('apps/{apps}/houses/colour/{colour}', 'HouseController#colourCheck');
Due to this odd behaviour, some routes were returning null for the parameter name apps and some where showing the parameter. Once I renamed all route parameters to {app} in the routes/api.php and Middleware, the Middleware started working the way it should.
I don't know why the resource routes were creating the parameter as app, but the above fixed the error for me, atleast for now.
There are two ways for accessing parameters in a middleware:
Method 1
$request->route('parameter_name');
Here parameter_name refers to what we called the parameter in the route.
Method 2
$request->route()->parameters();
This method will return an array of all the parameters.
Resource Parameters Are Singular By Default
If you would like to maintain the previous behavior instead of automatically singularizing resource route parameters, you may make the following call to the singularResourceParameters method in your AppServiceProvider:
use Illuminate\Support\Facades\Route;
Route::singularResourceParameters(false);
For me, this was the solution for Laravel 6.
In the routes file add ->middleware() like this:
Route::resource('orders', 'OrdersController')->middleware('roles:Admin,System');
Now to retrieve the parameters in the middleware class:
public function handle($request, Closure $next, ...$roles)
{
// now $roles contains: ['Admin, 'System']
}

Resources