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

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

Related

best way to log every request/response

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]);
}

Custom Middleware Not working when using Sanctum

I am using Dynamic database for my laravel API Project. I am doing that with a middleware. When I use auth:sanctum in api rout middleware not working. But normally its working fine.
Route> api.php
Route::group(['middleware'=>'database'], function() {
Route::group(['namespace' => 'App\Http\Controllers\Admin', 'middleware' => ['auth:sanctum']], function () {
Route::get('dashboard', function () {
return "conncted sucessfully to database ".\DB::connection()->getDatabaseName();
});
});
});
Middleware > Database.php
namespace App\Http\Middleware;
use Closure;
use App\Models\SoftClient;
class Database
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$route = app('router')->getRoutes()->match($request);
$prefix = $route->action['name'];
$s_client = SoftClient::where('url', $prefix)->first();
\Config::set('database.connections.multidomain.host', $s_client->db_host );
\Config::set('database.connections.multidomain.database', $s_client->db_name );
\Config::set('database.connections.multidomain.username', $s_client->db_username );
\Config::set('database.connections.multidomain.password', $s_client->db_password );
\DB::reconnect('multidomain');
return $next($request);
}
}
'database' => \App\Http\Middleware\Database::class,
I add this to Karnel.php
When I send request to dashboard I got bellow error. If I not use auth:sanctum It's working fine.
Illuminate\Database\QueryException: SQLSTATE[HY000] [1045] Access denied for user ''#'localhost' (using password: NO)
auth sanctum is used to authenticate user. Maybe you are directly accessing the routes with authenticating user.
when you check the routes with "php artisan route:list", do you see the middleware you configured with the route?
I had a similar problem, where route:list pointed me to the fact that the middleware I defined with the group wasn't actually used, and in the end I dropped the "group" and just defined the routes individually. A bit cumbersome but it worked.
if your goal is to use laravel sanctum for dynamic database connection then i use this method and it works
step 1 : copy this file Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful to our middlewear and then modify duplicate this file like this
<?php
namespace App\Http\Middleware;
use Illuminate\Routing\Pipeline;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\App;
class EnsureFrontendRequestsAreStateful
{
/**
* Handle the incoming requests.
*
* #param \Illuminate\Http\Request $request
* #param callable $next
* #return \Illuminate\Http\Response
*/
public function handle($request, $next)
{
$this->changeDatabaseConnection($request);
$this->configureSecureCookieSessions();
return (new Pipeline(app()))->send($request)->through(static::fromFrontend($request) ? [
function ($request, $next) {
$request->attributes->set('sanctum', true);
return $next($request);
},
config('sanctum.middleware.encrypt_cookies', \Illuminate\Cookie\Middleware\EncryptCookies::class),
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
config('sanctum.middleware.verify_csrf_token', \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class),
] : [])->then(function ($request) use ($next) {
return $next($request);
});
}
protected function changeDatabaseConnection($request)
{
$encodeCc = $request->input('cc');
$cc = json_decode(base64_decode($encodeCc));
if ((!empty($cc) && $cc != 'axiasolusi')) {
$compCode = $cc;
if (App::environment() === 'local') {
$compCode = 'ess-' . $compCode;
}
} else {
$compCode = env('DB_DATABASE');
}
Config::set('database.connections.' . env("DB_CONNECTION") . '.database', $compCode);
}
/**
* Configure secure cookie sessions.
*
* #return void
*/
protected function configureSecureCookieSessions()
{
config([
'session.http_only' => true,
'session.same_site' => 'lax',
]);
}
/**
* Determine if the given request is from the first-party application frontend.
*
* #param \Illuminate\Http\Request $request
* #return bool
*/
public static function fromFrontend($request)
{
$domain = $request->headers->get('referer') ?: $request->headers->get('origin');
if (is_null($domain)) {
return false;
}
$domain = Str::replaceFirst('https://', '', $domain);
$domain = Str::replaceFirst('http://', '', $domain);
$domain = Str::endsWith($domain, '/') ? $domain : "{$domain}/";
$stateful = array_filter(config('sanctum.stateful', []));
return Str::is(Collection::make($stateful)->map(function ($uri) {
return trim($uri) . '/*';
})->all(), $domain);
}
}
step 2 : change karnel like this
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, // original
\App\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
and last step : change route api
Route::middleware('change.database')->group(function () {
Route::post('/verify-company-code', 'Api\Web\Auth#verifyCompanyCode');
Route::post('/verify-user-account', 'Api\Web\Auth#verifyUserAccount');
Route::post('/verify-user-pin', 'Api\Web\Auth#verifyUserPin');
Route::post('/forgot-password', 'Api\Web\Auth#forgotPassword');
Route::post('/reset-password', 'Api\Web\Auth#resetPassword');
});
Route::middleware(['auth:sanctum'])->group(function () {
Route::post('logout', [Auth::class, 'logout']);
Route::post('logout-all', 'Api\Web\Auth#logoutAll');
});
});

NotFoundHttpException with my Request file on api route

I made a route in the api file that allow everybody to create users :
Route::post('users', 'UserController#addUser');
When I called it in postman without using request validation it works. But when I created my request file and use it, Laravel return a NotFoundHttpException.
Here's my request file :
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UserAddRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'user_id' => 'required|numeric',
'name' => 'required|string',
'email' => 'required|string',
'password' => 'required|string'
];
}
}
public function addUser(UserAddRequest $request){
$user = new User;
$user->instance_user_id = $request->input('user_id');
$user->name = $request->input('name');
$user->email = $request->input('email');
$user->password = $request->input('password');
$user->save();
}
There is no form because it needs to be send directly to the server with post method. I declared my route in routes/api.php and I call it with the url /api/users The api in that case doesn't need to check credentials.
I solved my problem :
The NotFoundHttpException was raised because I didn't send my parametters correctly and Laravel doesn't where to redirect me back. When I send directly the request to the server there is no url declared for where I'm coming.

Laravel owner middleware not working

I created a middleware (app/Http/Middleware/AbortIfNotOwner.php), this is code from another Stackoverflow post
<?php
namespace App\Http\Middleware;
use Closure;
use DB;
class AbortIfNotOwner
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string $resourceName
* #return mixed
*/
public function handle($request, Closure $next, $resourceName)
{
$resourceId = $request->route()->parameter($resourceName);
$user_id = \DB::table($resourceName)->find($resourceId)->user_id;
if ($request->user()->id != $user_id) {
abort(403, 'Unauthorized action.');
}
return $next($request);
}
}
I register it in the app\Http\Kernel.php
protected $routeMiddleware = [
'owner' => 'App\Http\Middleware\AbortIfNotOwner',
];
and in my route file I have:
Route::group(['middleware' => ['owner:bids']], function() {
Route::get('user/{id}/bids', ['as' => 'buyer_bids', 'uses' => 'User\Buyer\BidsController#getBidsPerUser']);
});
When I run this code I get an
ErrorException in AbortIfNotOwner.php line 23: Trying to get property
of non-object
This refers to the following lines in the middleware:
> $resourceId = $request->route()->parameter($resourceName);
> $user_id = \DB::table($resourceName)->find($resourceId)->user_id;
The issue seems to be that resourceId is null I think. I do have a field user_id in the bids table, so am not sure what is wrong. The route URL is like /user/2/bids.
EDIT - SOLVED
I found that the below works:
$user_id = \DB::table($resourceName)->find($request->id)->user_id;
instead of
$resourceId = $request->route()->parameter($resourceName);
$user_id = \DB::table($resourceName)->find($resourceId)->user_id;
This works with the routes like
Route::get('/{id}/bids'
EDIT - SOLVED - Other solution
$resourceId = $request->route()->parameter($resourceName);
$user_id = \DB::table($resourceName)->find($resourceId)->user_id;
will work if the route is changed to
Route::get('/{bids}/bids'
... instead of
Route::get('/{id}/bids'...
Change your route like this Route::get('user/{bids}/bids'... becasse to get a route parameter like this $request->route()->parameter('name') it has to match the parameter name in the route, meaning the bolded one user/{bids}/bids.
\DB::table($resourceName)->find($resourceId) is returning null (no results), so there's no user_id property to it. Check that you've found a result before attempting to access its properties.
Same thing for $request->user()->id - if $request->user() is null due to the user not being logged in, it'll fail.

Laravel middleware 'except' rule not working

I have a controller with the following in the constructor:
$this->middleware('guest', ['except' =>
[
'logout',
'auth/facebook',
'auth/facebook/callback',
'auth/facebook/unlink'
]
]);
The 'logout' rule (which is there by default) works perfectly but the other 3 rules I have added are ignored. The routes in routes.php look like this:
Route::group(['middleware' => ['web']],function(){
Route::auth();
// Facebook auth
Route::get('/auth/facebook', 'Auth\AuthController#redirectToFacebook')->name('facebook_auth');
Route::get('/auth/facebook/callback', 'Auth\AuthController#handleFacebookCallback')->name('facebook_callback');
Route::get('/auth/facebook/unlink', 'Auth\AuthController#handleFacebookUnlink')->name('facebook_unlink');
}
If I visit auth/facebook, auth/facebook/callback or auth/facebook/unlink whilst logged in I get denied by the middleware and thrown back to the homepage.
I've tried specifying the 'except' rules with proceeding /'s so they match the routes in routes.php exactly but it makes no difference. Any ideas why these rules are being ignored, whilst the default 'logout' rule is respected?
Cheers!
You need to pass the method's name instead of the URI.
<?php
namespace App\Http\Controllers;
class MyController extends Controller {
public function __construct() {
$this->middleware('guest', ['except' => [
'redirectToFacebook', 'handleFacebookCallback', 'handleFacebookUnlink'
]]);
}
}
Since Laravel 5.3, you can use fluent interface to define middlewares on controllers, which seems cleaner than using multidimensional arrays.
<?php
$this->middleware('guest')->except('redirectToFacebook', 'handleFacebookCallback', 'handleFacebookUnlink');
I solved this issue in my Middleware by adding this inExceptArray function. It's the same way VerifyCsrfToken handles the except array.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class MyMiddleware
{
/**
* Routes that should skip handle.
*
* #var array
*/
protected $except = [
'/some/route',
];
/**
* Determine if the request has a URI that should pass through.
*
* #param Request $request
* #return bool
*/
protected function inExceptArray($request)
{
foreach ($this->except as $except) {
if ($except !== '/') {
$except = trim($except, '/');
}
if ($request->is($except)) {
return true;
}
}
return false;
}
/**
* Handle an incoming request.
*
* #param Request $request
* #param Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// check user authed or API Key
if (!$this->inExceptArray($request)) {
// Process middleware checks and return if failed...
if (true) {
// Middleware failed, send back response
return response()->json([
'error' => true,
'Message' => 'Failed Middleware check'
]);
}
}
// Middleware passed or in Except array
return $next($request);
}
}
If you are trying to follow the Laravel Documentation, an alternative solution to this is suggested by adding routes to the $except variable in the /Http/Middleware/VerifyCsrfToken.php file. The documentation says to add them like this:
'route/*'
But I found the only way to get it to work is by putting the routes to ignore like this:
'/route'
When assigning middleware to a group of routes, you may occasionally need to prevent the middleware from being applied to an individual route within the group. You may accomplish this using the withoutMiddleware method:
use App\Http\Middleware\CheckAge;
Route::middleware([CheckAge::class])->group(function () {
Route::get('/', function () {
//
});
Route::get('admin/profile', function () {
//
})->withoutMiddleware([CheckAge::class]);
});
for more information read documentation laravel middleware
Use this function in your Controller:
public function __construct()
{
$this->middleware(['auth' => 'verified'])->except("page_name_1", "page_name_2", "page_name_3");
}
*replace page_name_1/2/3 with yours.
For me it's working fine.
I have this solved, and here's what I am doing. Aso, I just realized this is very similar to what cmac did in his answer.
api.php
Route::group(['middleware' => 'auth'], function () {
Route::get('/user', 'Auth\UserController#me')->name('me');
Route::post('logout', 'Auth\LoginController#logout')->name('logout');
});
LoginController.php
class LoginController extends Controller
{
use AuthenticatesUsers, ThrottlesLogins;
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
// ...
/**
* If the user's session is expired, the auth token is already invalidated,
* so we just return success to the client.
*
* This solves the edge case where the user clicks the Logout button as their first
* interaction in a stale session, and allows a clean redirect to the login page.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
$user = $this->guard()->user();
if ($user) {
$this->guard()->logout();
JWTAuth::invalidate();
}
return response()->json(['success' => 'Logged out.'], 200);
}
}
Authenticate.php
class Authenticate extends Middleware
{
/**
* Exclude these routes from authentication check.
*
* Note: `$request->is('api/fragment*')` https://laravel.com/docs/7.x/requests
*
* #var array
*/
protected $except = [
'api/logout',
];
/**
* Ensure the user is authenticated.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
try {
foreach ($this->except as $excluded_route) {
if ($request->path() === $excluded_route) {
\Log::debug("Skipping $excluded_route from auth check...");
return $next($request);
}
}
// code below here requires 'auth'
{ catch ($e) {
// ...
}
}
I over-engineered it slightly. Today I only need an exemption on /api/logout, but I set the logic up to quickly add more routes. If you research the VerifyCsrfToken middleware, you'll see it takes a form like this:
protected $except = [
'api/logout',
'api/foobars*',
'stripe/poop',
'https://www.external.com/yolo',
];
That's why I put that "note" in my doc above there. $request->path() === $excluded_route will probably not match api/foobars*, but $request->is('api/foobars*') should. Additionally, a person might be able to use something like $request->url() === $excluded_route to match http://www.external.com/yolo.
You should pass the function name to 'except'.
Here's an example from one of my projects:
$this->middleware('IsAdminOrSupport', ['except' => [
'ProductsByShopPage'
]
]);
This means the middleware 'IsAdminOrSupport' is applied to all methods of this controller except for the method 'ProductByShopPage'.

Resources