I'm using Laravel 5.1 and trying to set different logging logic for a development and production environment.
Throughout my application I am using the Log facade with most of the following different methods:
Log::emergency($error);
Log::alert($error);
Log::critical($error);
Log::error($error);
Log::warning($error);
Log::notice($error);
Log::info($error);
Log::debug($error);
However, in my production environment, I would like to only log anything that is an Error, Critical, Alert or Emergency priority and ignore log requests with lower priority.
I couldn't find anything in the documentation or by exploring the code (both Log facade and the Monolog class).
My current thought is to create a custom wrapper around the Log facade that simply checks the environment and ignores anything below 400 (Monolog level for Error). Basically I would create a threshold variable in the environment file and anything below it will simply not be logged to the files.
Before I do so, I wanted to ask the community if there is an existing method/configuration for that which I could use, so that I don't re-invent the wheel.
If not - what would be the best approach?
This gist shows a more comfortable answer, as is not dependent on the
chosen handler.
I'm just providing the essential part in an answer here in case the above link gets deleted in some time.
In the AppServiceProviders' register method:
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
$monolog = Log::getMonolog();
foreach($monolog->getHandlers() as $handler) {
$handler->setLevel(Config::get('app.log-level'));
}
}
Then just add an additional key to your config/app.php:
'log-level' => 'info', // or whatever minimum log level you would like.
Add the following code to your AppServiceProvider::register():
$this->app->configureMonologUsing(function ($monolog) {
$monolog->pushHandler(
$handler = new RotatingFileHandler(
$this->app->storagePath() . '/logs/laravel.log',
$this->app->make('config')->get('app.log_max_files', 5),
$this->app->make('config')->get('app.level', 'debug')
)
);
$handler->setFormatter(new LineFormatter(null, null, true, true));
});
This recreates the logic that Laravel does when setting up the daily handler, but adds passing level to the handler.
You can set your minimum logging level by setting level value in your config/app.php:
'level' => 'debug', //debug, info, notice, warning, error, critical, alert, emergency
This is a bit of a workaround and each type of handler would need to be set up separately. I'm currently working on a pull-request to Laravel that would add setting minimum debug level from the config file without writing a line of code in your AppServiceProvider.
The code above hasn't been tested, so let me know if you see any typos or something doesn't work properly and I'll be more than happy to make that work for you.
Related
I have asked this question yesterday as well, but this one includes code.
Issue
My application have multiple modules and 2 types of user accounts, Some modules are loaded always which are present in application.config.php some of them are conditional i.e. some are loaded for user type A and some for user type B
After going through documentations and questions on Stack Overflow, I understand some of ModuleManager functionalities and started implementing the logic that I though might work.
Some how I figured out a way to load the modules that are not present in application.config.php [SUCCESS] but their configuration is not working [THE ISSUE] i.e. if in onBootstrap method I get the ModuleManager service and do getLoadedModules() I get the list of all the modules correctly loaded. Afterwards if I try to get some service from that dynamically loaded module, it throws exception.
Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for jobs_mapper
Please note that, the factories and all other stuff are perfectly fine because if I load the module from application.config.php it works fine
Similarly when I try to access any route from the dynamically loaded module it throws 404 Not Found which made it clear that the configuration from module.config.php of these modules are not loading even though the module is loaded by ModuleManager.
Code
In Module.php of my Application module I implemented InitProviderInterface and added a method init(ModuleManager $moduleManager) where I catch the moduleManager loadModules.post event trigger and load modules
public function init(\Zend\ModuleManager\ModuleManagerInterface $moduleManager)
{
$eventManager = $moduleManager->getEventManager();
$eventManager->attach(\Zend\ModuleManager\ModuleEvent::EVENT_LOAD_MODULES_POST, [$this, 'onLoadModulesPost']);
}
Then in the same class I delcare the method onLoadModulesPost and start loading my dynamic modules
public function onLoadModulesPost(\Zend\ModuleManager\ModuleEvent $event)
{
/* #var $serviceManager \Zend\ServiceManager\ServiceManager */
$serviceManager = $event->getParam('ServiceManager');
$configListener = $event->getConfigListener();
$authentication = $serviceManager->get('zfcuser_auth_service');
if ($authentication->getIdentity())
{
$moduleManager = $event->getTarget();
...
...
$loadedModules = $moduleManager->getModules();
$configListener = $event->getConfigListener();
$configuration = $configListener->getMergedConfig(false);
$modules = $modulesMapper->findAll(['is_agency' => 1, 'is_active' => 1]);
foreach ($modules as $module)
{
if (!array_key_exists($module['module_name'], $loadedModules))
{
$loadedModule = $moduleManager->loadModule($module['module_name']);
//Add modules to the modules array from ModuleManager.php
$loadedModules[] = $module['module_name'];
//Get the loaded module
$module = $moduleManager->getModule($module['module_name']);
//If module is loaded succesfully, merge the configs
if (($loadedModule instanceof ConfigProviderInterface) || (is_callable([$loadedModule, 'getConfig'])))
{
$moduleConfig = $module->getConfig();
$configuration = ArrayUtils::merge($configuration, $moduleConfig);
}
}
}
$moduleManager->setModules($loadedModules);
$configListener->setMergedConfig($configuration);
$event->setConfigListener($configListener);
}
}
Questions
Is it possible to achieve what I am trying ?
If so, what is the best way ?
What am I missing in my code ?
I think there is some fundamental mistake in what you are trying to do here: you are trying to load modules based on merged configuration, and therefore creating a cyclic dependency between modules and merged configuration.
I would advise against this.
Instead, if you have logic that defines which part of an application is to be loaded, put it in config/application.config.php, which is responsible for retrieving the list of modules.
At this stage though, it is too early to depend on any service, as service definition depends on the merged configuration too.
Another thing to clarify is that you are trying to take these decisions depending on whether the authenticated user (request information, rather than environment information) matches a certain criteria, and then modifying the entire application based on that.
Don't do that: instead, move the decision into the component that is to be enabled/disabled conditionally, by putting a guard in front of it.
What you're asking can be done, but that doesn't mean you should.
Suggesting an appropriate solution without knowing the complexity of the application you're building is difficult.
Using guards will certainly help decouple your code, however using it alone doesn't address scalability and maintainability, if that's a concern?
I'd suggest using stateless token-based authentication. Instead of maintaining the validation logic in every application, write the validation logic at one common place so that every request can make use of that logic irrespective of application. Choosing a reverse proxy server (Nginx) to maintain the validation logic (with the help of Lua) gives you the flexibility to develop your application in any language.
More to the point, validating the credentials at the load balancer level essentially eliminates the need for the session state, you can have many separate servers, running on multiple platforms and domains, reusing the same token for authenticating the user.
Identifying the user, account type and loading different modules then becomes a trivial task. By simply passing the token information via an environment variable, it can be read within your config/application.config.php file, without needing to access the database, cache or other services beforehand.
I'm using configureMonologUsing() to add in two custom loggers. Doing the standard SOLID principal, I have two providers: ConsoleLoggerProvider and MailLogProvider.
Both of these have a register similar to:
public function register()
{
app()->configureMonologUsing(function(\Monolog\Logger $monolog) {
$monolog->pushHandler(new HandlerClass());
});
}
However, I have noticed over logger will overwrite another logger... How do I stack these?
I've tried to use boot() as well, and that didn't work. I couldn't find any other way to add to the Monolog stack.
Preferable, I want to stack onto Laravel's built-in logger as well.
I (finally) found the answer my question:
Within my providers, instead of using configureMonologUsing(), I used Log::getMonolog()->pushHandler([..])
That works! All loggers, including built-in Laravel file logger, are firing. Finally!
(I've honestly been looking for days for a way to add onto the Monolog stack; I apparently wasn't searching by the right terms)
According to the Laravel documentation:
You should place a call to the configureMonologUsing method in your bootstrap/app.php file right before the $app variable is returned by the file.
In that case, thus should work for you: create two handler classes and add them to monolog this way (in your bootstrap/app.php):
$app->configureMonologUsing(function ($monolog) {
$monolog->pushHandler(new EmailLogHandler);
$monolog->pushHandler(new ConsoleLogHandler);
});
return $app;
Following Laravel 5.2 docs, in bootstrap/app.php, I added the following code right before return $app;:
$app->configureMonologUsing(function($monolog) {//IMPORTANT: I think the order of pushHandler matters, and the ones defined last here will be the first to be called, which affects anything where bubble=false
if (config('services.slack.send_errors_to_slack')) {
$bubble = false; //I think that if I set the 'bubble' argument to false and handle the most severe logging levels first (which counterintuitively means lower in this function), less severe logging levels don't bother reporting the same message.
$useShortAttachment = false;
$includeContextAndExtra = true; //This is important because otherwise 404 errors wouldn't report the URL, give how 'report' function is coded within App\Exceptions\Handler.php.
$handlerForWarningsToNotifyPhone = new \Monolog\Handler\SlackHandler(config('services.slack.token'), config('services.slack.channel_warnings'), 'Monolog', true, null, \Monolog\Logger::WARNING, $bubble, $useShortAttachment, $includeContextAndExtra);
$monolog->pushHandler($handlerForWarningsToNotifyPhone);
$handlerForErrorsToNotifyPhone = new \Monolog\Handler\SlackHandler(config('services.slack.token'), config('services.slack.channel_errors'), 'Monolog', true, null, \Monolog\Logger::ERROR, $bubble, $useShortAttachment, $includeContextAndExtra);
$monolog->pushHandler($handlerForErrorsToNotifyPhone);
}
if (config('app.send_logs_to_loggy')) {
$logglyHandler = new \Monolog\Handler\LogglyHandler(config('services.loggly.token'), config('app.send_logs_to_loggy')); //See \Monolog\Logger::INFO. Log level 200 is "info".
$logglyHandler->setTag(config('services.loggly.tag'));
$monolog->pushHandler($logglyHandler);
}
if (config('app.log_to_local_disk')) {
$localHandler = new \Monolog\Handler\StreamHandler(storage_path("/logs/laravel.log"));
$monolog->pushHandler($localHandler);
}
});
It's just an example that may help you.
Be sure to edit your config files accordingly (e.g. so that app.log_to_local_disk, services.slack.send_errors_to_slack, etc are available).
http://stackoverflow.com/a/36259944/470749 was helpful.
Here is how I able to configure on Laravel Lumen v5.4
in app.php:
$publisher = new \Gelf\Publisher(new \Gelf\Transport\HttpTransport(env('GRAYLOG_HOST'), env('GRAYLOG_PORT'), env('GRAYLOG_PATH')));
//WhatFailureGroupHandler does not break app execution
//if some exceptions happen happens while logging
$failureHandler = new \Monolog\Handler\WhatFailureGroupHandler([
new \Monolog\Handler\GelfHandler($publisher)
]);
\Log::pushHandler($failureHandler);
\Log::getMonolog() as on accepted answer threw error.
Also tried to configure using $app->configureMonologUsing() which threw A facade root has not been set. error. But at the end, I found out that was because we need to return logger:
$app->configureMonologUsing(function ($monolog) {
$publisher = new \Gelf\Publisher(new \Gelf\Transport\HttpTransport(env('GRAYLOG_HOST'), env('GRAYLOG_PORT'), env('GRAYLOG_PATH')));
$failureHandler = new \Monolog\Handler\WhatFailureGroupHandler([new \Monolog\Handler\GelfHandler($publisher)]);
$monolog->pushHandler($failureHandler);
//fixes error: A facade root has not been set
return $monolog;
});
All the examples of $app->configureMonologUsing() usage I have seen do not have a return statement, even in the other answers, which did not work for me.
I have created different files for different level of logging e.g Log::info, Log::error etc. Now I also want to send a message to slack only if an error comes. I have done this too by writing following code in app/bootstrap.php
$app->configureMonologUsing( function($monolog) {
$slackHandler = new Monolog\Handler\SlackHandler(env('SLACK_TOKEN'), env('SLACK_CHANNEL'), 'Monolog', true, null, Monolog\Logger::ERROR);
$monolog->pushHandler($slackHandler);
});
but it has overrided the default logger and now only slack logger is working and no logs are adding in storage/logs/ folder
The docs are pretty vague on what exactly this does but it looks like configureMonologUsing configures Monolog to use only the handlers which you specifically add to it. This means the handlers (I believe StreamHandler) which Laravel sets up to log to the laravel.log file are never added.
I'd recommend not using that and dropping this code into your AppServiceProvider which will add the new handler while keeping all the default handlers Laravel likes to add...
$monolog = \Log::getMonolog();
$slackHandler = new \Monolog\Handler\SlackHandler(env('SLACK_TOKEN'), env('SLACK_CHANNEL'), 'Monolog', true, null, Monolog\Logger::ERROR);
$monolog->pushHandler($slackHandler);
I'm using Monolog in a project, it's not Symfony, just my own application that uses the stand-alone Monolog composer package.
What I'd like to do is programmatically turn off debugging logs. I'm writing to a log file and I'm using the Monolog::StreamHandler. I'm controlling whether the application is in debug mode or not with a Configuration class that gets the debug value from a configuration file. So when someone changes that value to debugging is false, debug logging should turn off.
I felt like the easiest way to do this would be to extend StreamHandler and override StreamHandler's write method like this.
class DurpLogger extends StreamHandler {
protected function write(array $record) {
if ($this->getLevel() == Durp::Debug && !Configuration::debug()) {
return;
}
parent::write($record);
}
}
So if a log request comes in and the log level for the handler is set to DEBUG and the application's Configuration::debug() is FALSE then just return without writing the log message. Otherwise, StreamHandler will do its thing.
I'm wondering if this is the best way to use Monolog or if there's perhaps a cleaner way to do this.
I envision there being a handler in my application for DEBUG, INFO, ERROR and whatever levels I might need for my application. Perhaps it makes sense to not rely on a Configuration::debug() that can only be TRUE or FALSE, but rather a Configuration::logLevel() that will allow me to more granularly control logging output.
But even still, does extending StreamHandler make the most sense when controlling Monolog at the application level?
UPDATE
Now, I'm thinking something like this, that uses level rather than just boolean debug.
class DurpLogger extends StreamHandler {
public function __construct() {
parent::__construct(Configuration::logFile(), Configuration::logLevel());
}
protected function write(array $record) {
if (!($this->getLevel() >= Configuration::logLevel())) {
return;
}
parent::write($record);
}
}
Then I'd use it in the application like this.
class Durp {
private $logger;
public function __construct() {
$this->logger = new Logger('durp-service');
$this->logger->pushHandler(new DurpLogger());
$this->logger->addDebug('Debugging enabled');
$this->logger->addInfo('Starting Durp');
}
}
I figured the StreamHandler handles the file writing stuff, so that's why I'm extending it. And if I turn up the log level in Configuration to Logger::INFO, the "Debugging enabled" message doesn't get logged.
Open to suggestions to make this better.
A common alternative would be to use the NullHandler instead of the StreamHandler.
Maybe switch between them depending on your condition like follows:
if (!Configuration::debug()) {
$logger->pushHandler(new \Monolog\Handler\NullHandler());
}
I would like to give you an example that is more adapted to your usage,
but I need to see some code in order to know how you use it.
Update
For the question about default format, the empty [] at end represent the extra data that can be added with log entries.
From #Seldaek (Monolog's owner) :
The default format of the LineFormatter is:
"[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n". the username/age is the context, and extra that is typically empty results in this empty array [].
If you use processors to attach data to log records they typically write it to the extra key to avoid conflicts with context info. If it really is an issue for you you can change the default format and omit %extra%.
Edit: As of Monolog 1.11 the LineFormatter has a $ignoreEmptyContextAndExtra parameter in the constructor that lets you remove these, so you can use this:
// the last "true" here tells it to remove empty []'s
$formatter = new LineFormatter(null, null, false, true);
$handler->setFormatter($formatter);
See How not to show last bracket in a monolog log line? and Symfony2 : use Processors while logging in different files about the processors which #Seldaek is talking about.
The Problem in a Nutshell
I'm looking for a way to remove VerifyCsrfToken from the global middleware pipeline from within a package without the user having to modify App\Http\Middleware\VerifyCsrfToken. Is this possible?
The Use Case
I'm developing a package that would make it easy to securely add push-to-deploy functionality to any Laravel project. I'm starting with Github. Github uses webhooks to notify 3rd party apps about events, such as pushes or releases. In other words, I would register a URL like http://myapp.com/deploy at Github, and Github will send a POST request to that URL with a payload containing details about the event whenever it happens, and I could use that event to trigger a new deployment. Obviously, I don't want to trigger a deployment on the off chance that some random (or perhaps malicious) agent other than the Github service hits that URL. As such, Github has a process for securing your webhooks. This involves registering a secret key with Github that they will use to send a special, securely hashed header along with the request that you can use to verify it.
My approach to making this secure involves:
Random Unique URL/Route and Secret Key
First, I automatically generate two random, unique strings, that are stored in the .env file and used to create a secret key route within my app. In the .env file this looks like:
AUTODEPLOY_SECRET=BHBfCiC0bjIDCAGH2I54JACwKNrC2dqn
AUTODEPLOY_ROUTE=UG2Yu8QzHY6KbxvLNxcRs0HVy9lQnKsx
The config for this package creates two keys, auto-deploy.secret and auto-deploy.route that I can access when registering the route so that it never gets published in any repo:
Route::post(config('auto-deploy.route'),'MyController#index');
I can then go to Github and register my webook like this:
In this way, both the deployment URL and the key used to authenticate the request will remain secret, and prevent a malicious agent from triggering random deployments on the site.
Global Middleware for Authenticating Webhook Requests
The next part of the approach involves creating a piece of global middleware for the Laravel app that would catch and authenticate the webhook requests. I am able to make sure that my middleware gets executed near the beginning of the queue by using an approach demonstrated in this Laracasts discussion thread. In the ServiceProvider for my package, I can prepend a new global middleware class as follows:
public function boot(Illuminate\Contracts\Http\Kernel $kernel)
{
// register the middleware
$kernel->prependMiddleware(Middleware\VerifyWebhookRequest::class);
// load my route
include __DIR__.'/routes.php';
}
My Route looks like:
Route::post(
config('auto-deploy.route'), [
'as' => 'autodeployroute',
'uses' => 'MyPackage\AutoDeploy\Controllers\DeployController#index',
]
);
And then my middleware would implement a handle() method that looks something like:
public function handle($request, Closure $next)
{
if ($request->path() === config('auto-deploy.route')) {
if ($request->secure()) {
// handle authenticating webhook request
if (/* webhook request is authentic */) {
// continue on to controller
return $next($request);
} else {
// abort if not authenticated
abort(403);
}
} else {
// request NOT submitted via HTTPS
abort(403);
}
}
// Passthrough if it's not our secret route
return $next($request);
}
This function works right up until the continue on to controller bit.
The Problem in Detail
Of course the problem here is that since this is a POST request, and there is no session() and no way to get a CSRF token in advance, the global VerifyCsrfToken middleware generates a TokenMismatchException and aborts. I have read through numerous forum threads, and dug through the source code, but I can't find any clean and easy way to disable the VerifyCsrfToken middleware for this one request. I have tried several workarounds, but I don't like them for various reasons.
Workaround Attempt #1: Have user modify VerifyCsrfToken middleware
The documented and supported method for solving this problem is to add the URL to the $except array in the App\Http\Middleware\VerifyCsrfToken class, e.g.
// The URIs that should be excluded from CSRF verification
protected $except = [
'UG2Yu8QzHY6KbxvLNxcRs0HVy9lQnKsx',
];
The problem with this, obviously, is that when this code gets checked into the repo, it will be visible to anyone who happens to look. To get around this I tried:
protected $except = [
config('auto-deploy.route'),
];
But PHP didn't like it. I also tried using the route name here:
protected $except = [
'autodeployroute',
];
But this doesn't work either. It has to be the actual URL. The thing that actually does work is to override the constructor:
protected $except = [];
public function __construct(\Illuminate\Contracts\Encryption\Encrypter $encrypter)
{
parent::__construct($encrypter);
$this->except[] = config('auto-deploy.route');
}
But this would have to be part of the installation instructions, and would be an unusual install step for a Laravel package. I have a feeling this is the solution I'll end up adopting, as I guess it's not really that difficult to ask users to do this. And it has the upside of at least possibly making them conscious that the package they're about to install circumvents some of Laravel's built in security.
Workaround Attempt #2: catch the TokenMismatchException
The next thing I tried was to see if I could just catch the exception, then ignore it and move on, i.e.:
public function handle($request, Closure $next)
{
if ($request->secure() && $request->path() === config('auto-deploy.route')) {
if ($request->secure()) {
// handle authenticating webhook request
if (/* webhook request is authentic */) {
// try to continue on to controller
try {
// this will eventually trigger the CSRF verification
$response = $next($request);
} catch (TokenMismatchException $e) {
// but, maybe we can just ignore it and move on...
return $response;
}
} else {
// abort if not authenticated
abort(403);
}
} else {
// request NOT submitted via HTTPS
abort(403);
}
}
// Passthrough if it's not our secret route
return $next($request);
}
Yeah, go ahead and laugh at me now. Silly wabbit, that's not how try/catch works! Of course $response is undefined within the catch block. And If I try doing $next($request) in the catch block, it just bangs up against the TokenMismatchException again.
Workaround Attempt #3: Run ALL of my code in the middleware
Of course, I could just forget about using a Controller for the deploy logic and trigger everything from the middleware's handle() method. The request lifecycle would end there, and I would never let the rest of the middleware propagate. I can't help feeling that there's something inelegant about that, and that it departs from the overall design patterns upon which Laravel is built so much that it would end up making maintenance and collaboration difficult moving forward. At least I know it would work.
Workaround Attempt #4: Modify the Pipeline
Philip Brown has an excellent tutorial describing the Pipeline pattern and how it gets implemented in Laravel. Laravel's middleware uses this pattern. I thought maybe, just maybe, there was a way to get access to the Pipeline object that queues up the middleware packages, loop through them, and remove the CSRF one for my route. Best I can tell, there are ways to add new elements to the pipeline, but no way to find out what's in it or to modify it in any way. If you know of a way, please let me know!!!
Workaround Attempt #5: Use the WithoutMiddleware trait
I haven't investigated this one quite as thoroughly, yet, but it appears that this trait was added recently to allow testing routes without having to worry about middleware. It's clearly NOT meant for production, and disabling the middleware would mean that I'd have to come up with a whole new solution for figuring out how to get my package to do its thing. I decided this was not the way to go.
Workaround Attempt #6: Give up. Just use Forge or Envoyer
Why reinvent the wheel? Why not just pay for one or both of these service that already supports push-to-deploy rather than go to the trouble of rolling my own package? Well, for one, I only pay $5/month for my server, so somehow the economics of paying another $5 or $10 per month for one of these services doesn't feel right. I'm a teacher who builds apps to support my teaching. None of them generate revenue, and although I could probably afford it, this kinda thing adds up over time.
Discussion
Okay, so I've spent the better part of two solid days banging my head against this problem, which is what brought me here looking for help. Do you have a solution? If you've read this far, perhaps you'll indulge a couple of closing thoughts.
Thought #1: Bravo to the Laravel guys for taking security seriously!
I'm really impressed with how difficult it is to write a package that circumvents the built-in security mechanisms. I'm not talking about "circumvention" in the I'm-trying-to-do-something-bad way, but in the sense that I'm trying to write a legitimate package that would save me and lots of other people time, but would, in effect, be asking them to "trust me" with the security of their applications by potentially opening them up to malicious deployment triggers. This should be tough to get right, and it is.
Thought #2: Maybe I shouldn't be doing this
Frequently if something is hard or impossible to implement in code, that is by design. Maybe it's Bad Design™ on my part to want to automate the entire installation process for this package. Maybe this is the code telling me, "Don't do that!" What do you think?
In summary, here are two questions:
Do you know a way to do this that I haven't thought of?
Is this bad design? Should I not do it?
Thanks for reading, and thank you for your thoughtful answers.
P.S. Before someone says it, I know this might be a duplicate, but I provided much more detail than the other poster, and he never found a solution, either.
I know it is not good practice to use the Reflection API in production code, but this is the only solution i could think of where no additional configuration is needed. This is more like a proof of concept and I would not use it in production code.
I think a better and more stable solution is to have the user update his middleware to work with your package.
tl;dr - you can place this in your packages boot code:
// Just remove CSRF middleware when we hit the deploy route
if(request()->is(config('auto-deploy.route')))
{
// Create a reflection object of the app instance
$appReflector = new ReflectionObject(app());
// When dumping the App instance, it turns out that the
// global middleware is registered at:
// Application
// -> instances
// -> Illuminate\Contracts\Http\Kernel
// -> ... Somewhere in the 'middleware' array
//
// The 'instance' property of the App object is not accessible
// by default, so we have to make it accessible in order to
// get and set its value.
$instancesProperty = $appReflector->getProperty('instances');
$instancesProperty->setAccessible(true);
$instances = $instancesProperty->getValue(app());
$kernel = $instances['Illuminate\Contracts\Http\Kernel'];
// Now we got the Kernel instance.
// Again, we have to set the accessibility of the instance.
$kernelReflector = new ReflectionObject($kernel);
$middlewareProperty = $kernelReflector->getProperty('middleware');
$middlewareProperty->setAccessible(true);
$middlewareArray = $middlewareProperty->getValue($kernel);
// The $middlewareArray contains all global middleware.
// We search for the CSRF entry and remove it if it exists.
foreach ($middlewareArray as $i => $middleware)
{
if ($middleware == 'App\Http\Middleware\VerifyCsrfToken')
{
unset($middlewareArray[ $i ]);
break;
}
}
// The last thing we have to do is to update the altered
// middleware array on the Kernel instance.
$middlewareProperty->setValue($kernel, $middlewareArray);
}
I haven't tested this with Laravel 5.1 - for 5.2 it works.
So you could create a Route::group where you can explicitly say which middleware you want to use.
For example in your ServiceProvider you could do something like this:
\Route::group([
'middleware' => ['only-middleware-you-need']
], function () {
require __DIR__ . '/routes.php';
});
So just exclude VerifyCsrfToken middleware, and put what you need.