best way to log every request/response - laravel

I have a POST API to create a register on the database. My goal is to be able to log every 4XX request (with the response) for another team to view that list/search the data, with the option to download the request JSON sent in that call.
What's the best way to archive that? Its just to create a logs table in the database?

You might want to leverage the ResponseReceived event. We can create a LogResponseReceived listener:
use Illuminate\Http\Client\Events;
class LogResponseReceived
{
public function handle(ResponseReceived $event)
{
if ($event->request->method() == 'POST' && $event->request->url() == $yourUrl && $response->status() == $yourStatusCode) {
// Log the data.
}
}
}
In addition, you can use laravel/telescope, But that is going to log every request.

Telescope may not be a very logical choice for use in a production environment. You can find an answer to your problem with a simple middleware. If it is an endpoint that receives a lot of requests, logging to the database will not be performant enough. You can write to a different log file instead.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Log;
class RequestLogger
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request);
//here you can check the request to be logged
$log = [
'URI' => $request->getUri(),
'METHOD' => $request->getMethod(),
'REQUEST_BODY' => $request->all(),
'RESPONSE' => $response->getContent()
];
return $response;
}
}

hi for this not really need to create a new table. you can use laravel logging. it's up to you to create daily logs or a single log file.
here are some examples depending on the type of log you want to save
use Illuminate\Support\Facades\Log;
Log::warning('User is accessing something', ['user' => Auth::user()]);
Log::info('User is accessing ', ['user' => Auth::user()]);
Log::emergency($message);
Log::alert($message);
Log::critical($message);
Log::error($message);
Log::notice($message);
Log::debug($message);
example
public function index(Request $request)
{
$todos = Todo::all();
Log::warning('User is accessing ', ['user' => Auth::user()]);
return view('dashboard')->with(['todos' => $todos]);
}

Related

After giving client expire date middleware validation return redirect to dashboard not working properly

I have created a middleware, the middleware name is expireDateCheck
This middleware class I have kept to Kernel.php and I've added to this class protected $routeMiddleware.
Inside Kernel.php code is:
protected $routeMiddleware = [ 'expireDateCheck' => \App\Http\Middleware\expireDateCheck::class, ]
Then I've given a condition inside middleware expireDateCheck.php for redirecting to the dashboard, this condition working fine but the main problem is it's redirecting to the dashboard two times which means after showing dashboard then it's showing again dashboard page it seems like dashboard page loading multiple time.
How can I fix this problem?
Inside middleware expireDateCheck.php code is:
<?php
namespace App\Http\Middleware;
use Closure;
use App\User;
use App\Client;
use Auth;
use Redirect;
class expireDateCheck
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (Auth::check() && Auth::user()){
$currentDate = date('Y-m-d H:i:s');
$userExpireDate = Client::where('expire_date', '<' , $currentDate)->first();
if($userExpireDate){
return Redirect::to('dashboard');
}
return $next($request);
}
}
}
Here are my routes:#Nikolay
Route::group(['middleware' => 'expireDateCheck'],function(){
-------------------------------
-------------------------------
});
Route::get('dashboard','DashboardController#index')->middleware('admin');
The middleware is run on all urls, therefor also on the request when loading the dashboard. You can remove the middlewares when defining the routes, this will avoid it from loading twice, and since the logic is to return them to the dashboard it does not makes sense running it on the dashboard.
Route::get('dashboard', 'DashboardController#index')->withoutMiddleware(['expireDateCheck']);
Or by grouping multiple.
Route::group([
'excluded_middleware' => ['expireDateCheck'],
], function () {});

Segments are not getting shifted / Cannot get correct arguments in Controller

I am trying to implement a simple localization for an existing Laravel project.
Implementing Localization based on the following tutorial:
https://laraveldaily.com/multi-language-routes-and-locales-with-auth/
Here is the simplified code before localization implementation:
web.php
Route::get('/poll/{poll_id}', 'App\Http\Controllers\PollsController#view');
PollsController#view
public function view($poll_id){
echo "poll_id: ".$poll_id;
}
TEST
URL: http://domain.name/poll/1
RESULT: poll_id: 1
Here are the simplified changes required for localization and the result I get:
web.php
Route::group(['prefix' => '{locale}', 'where' => ['locale' => '[a-zA-Z]{2}'], 'middleware' => 'setlocale'], function() {
Route::get('/poll/{poll_id}', 'App\Http\Controllers\PollsController#view');
});
Middleware/SetLocale
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class SetLocale
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next){
app()->setLocale($request->segment(1));
return $next($request);
}
}
PollsController#view remained unchanged.
Now, when I open the following URL (http://domain.name/en/poll/1), the result is:
RESULT: poll_id: en
QUESTION
Is there a way to ignore "'prefix' => '{locale}'" in controller or get arguments somehow shifted so that in the controller I still get poll_id=1, not locale=en?
PS. The easiest fix would be to add another argument to PollsController#view in the following way, but it does not smell well and then I would need to add locale argument to all functions, although I do not use it there:
public function view($locale, $poll_id){
echo "poll_id: ".$poll_id;
}
In your middleware you can tell the Route instance to forget that route parameter so it won't try to pass it to route actions:
public function handle($request, $next)
{
app()->setLocale($request->route('locale'));
// forget the 'locale' parameter
$request->route()->forgetParameter('locale');
return $next($request);
}

Laravel 6 - How to Log All URL's of User Visited

I want to log all URL's that user visited. But there's not the url i want to logged from my code. Here are the code that im done, please give me some advise. Thanks and appreciate.
Web Routes
Route::get('/{url}', 'LogController#myTestAddToLog')->where('url', '[\w\d\-]+(.*)');
Log Controller
public function myTestAddToLog()
{
\LogActivity::addToLog('My Testing Add To Log.');
}
App/Helpers LogActivity
public static function addToLog($subject)
{
$log = [];
$log['url'] = Request::fullUrl();
$log['ip'] = Request::ip();
$log['user_id'] = auth()->check() ? auth()->user()->id : 1;
LogActivityModel::create($log);
}
You need to create a middleware.
php artisan make:middleware AddToLog
And then, put your code inside the middleware.
<?php
namespace App\Http\Middleware;
use Closure;
use App\LogActivityModel;
class AddToLog
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request);
if(auth()->user()) {
LogActivityModel::create([
'url' => request()->fullUrl(),
'ip' => request()->ip(),
'user_id' => auth()->id(),
]);
}
return $response;
}
}
Then, inside app/Http/kernel.php, add your middleware to $middleware. This would allow any URL called would trigger this code.
protected $middleware = [
...
\App\Http\Middleware\AddToLog::class,
];
For those who still has the issue I had modified a bit the answer provided by #Adlan and based on the comments of his answer. Since you will be logging the activities after the request has been performed, the accepted answer needs to be modified. Following is the modified code.
public function handle($request, Closure $next)
{
$response = $next($request);
if(auth()->user()) {
Activity::create([
'url' => request()->fullUrl(),
'ip_address' => request()->ip(),
'user_id' => auth()->id(),
]);
}
return $response;
}
First, the response for the next request is saved. Then user authentication is checked and finally, the response is returned.
Reference : https://laravel.com/docs/5.8/middleware
https://stackoverflow.com/a/59816949/7070809
Please use the below code to log all url that user has visited
Log::info(url()->full()); // For just loggin
And for storing user log create table store the url with timestamp and IP

Is this the correct way to make Laravel token middleware

I want to make my API unavailable to every client who doesn't have the token to access.
This means the Android app will send a client as android and token as token string in the header with keys client and token.
Now in middleware, I am checking it with my table fields to pass through authorization. If both matches then I will authorize and if don't then will send 403 response.
I am aware of Passport but it is not what I am looking for. In fact,
consider it as a first layer of security and then use Passport as a
second layer of security to authorize the API
Is this code is correct?
As I am not so familiar with Laravel - Middleware I just want to get some feedback from experts whether the code I have written is accurate and up to the standard. If not, I would appreciate your suggestion and help to make it better.
Middleware
namespace App\Http\Middleware;
use App\ApiToken;
use Closure;
use function response;
class ApiAccess
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
*
* #return mixed
*/
public function handle( $request, Closure $next ) {
if ( $this->checkToken( $request ) ) {
return $next( $request );
}
return response()->json( [ 'error' => 'Unauthorized' ], 403 );
}
public function checkToken( $request ) {
$client = $request->header( 'client' );
$token = $request->header( 'token' );
$checkToken = ApiToken::where( 'client', $client )
->where( 'token', $token )->first();
return $checkToken;
}
}
API Route
I am fetching result from the ApiToken table just to check.
Route::get('/', function(Request $request) {
return ApiToken::all();
})->middleware('apiAccess');
I can't add comment so writing here. It seems right to me but I don't think the extra layer is needed. Maybe your requirement needs it. Anyways, you can improve the checkToken function to return boolean value. Here it is:
public function checkToken( $request ) {
$client = $request->header( 'client' );
$token = $request->header( 'token' );
return ApiToken::where( 'client', $client )
->where( 'token', $token )->exists();
// Nicer, and it will return true and false based on the existence of the token and client.
}
One more thing, status code 403 is returned when the access is forbidden. 401 is returned with unauthorised error.

Laravel Custom Registration Route/Logic

I would like to overwrite Laravel 5.2's public function postRegister() in my Auth controller.
I start by changing the route:
Route::post('/auth/register', 'Auth\AuthController#postRegisterI');
In my Auth controller, I now have this new postRegisterI() method as oppose to relying on the foundational postRegister(). But my changes in this new method don't seem to apply?
Is it still pointing to postRegister() for some reason? My AuthController looks like this:
class AuthController extends Controller
{
/*
|--------------------------------------------------------------------------
| Registration & Login Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users, as well as the
| authentication of existing users. By default, this controller uses
| a simple trait to add these behaviors. Why don't you explore it?
|
*/
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
/**
* Create a new authentication controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest', ['except' => 'getLogout']);
}
public function postRegisterI(Request $request)
{
return print_r($request);
$validator = $this->validator($request->all());
if ($validator->fails()) {
$this->throwValidationException(
$request, $validator
);
}
//Auth::login($this->create($request->all()));
$this->create($request->all());
//return redirect($this->redirectPath());
return;
}
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255|unique:users',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:2',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
}
For example in my ajax requests to /auth/register/ it never echoes out my returns as I indicate and it also keeps trying to redirect me to /home. I'm doing this because instead of passing /auth/register a form, I'm passing it a Javascript array with data to not only create a user (email, password, etc) but also an application that the user needs to join.
Eventually, I would like to create the Application model with that data right when the User is created, but right now I'm not sure that my function is even being called correctly!
EDIT: So, it looks like the __construct() function is causing the issue... should I get rid of it? I don't understand why I need to comment that out to get my returns from postRegisterI()?
First, make sure that your new route is declared before the call to Route::auth(). Otherwise, Laravel will ignore your addition, and will always call the one that Easy Auth comes with.
Second. Why do you want to override a method using another method with a different name? By simply creating a method postRegister in your AuthController you are automatically overriding the method that resides in the Trait. So you shouldn't be worrying about having to add an additional method and route. Unless you want to have a multi-authentication system, and in this case, this wouldn't be the correct solution.
Another thing, for ajax request, make sure you validate that the request is of type ajax:
if ($request->ajax()){
//Code here
return response()->json([],200);
}
This controller is designed to check registration rules, create an user, and redirect to the redirection path. It is not designed for ajax handling, thus, you will need to add code for it
maybe this topic will help you custom register

Resources