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
Related
I would like to manually authenticate the users in my company. The issue is that, I have 2 tables, called Student and Staff in the Oracle database.
As for the Student table, I get the idea of overriding the built in Auth method provided through the auth scaffolding command as the username and password are stored right into the table.
As for the Staff table, the password is stored a different column/table and encrypted using a stored procedure/package so the only way to get the user validation is by calling the package which only returns 0 or 1 only.
What I have done,
I wrote my own Routes, and added my own functions in LoginController.
public function loginStaff(Request $req){
$username = Str::upper($req->input('username'));
$password = $req->input('password');
$users = PortalUser::where('ID', $username)->firstOrFail();
if ($users->user_type == 'STAFF'){
$queryResult = DB::select('select PACKAGE.validStaff(?,?) from dual',[$username, $password]);
if($queryResult == 1){
//this is where I would like to auth the user.
//using Auth::Attempt and Auth::Login will only run the default query
}
}
I have successfully returned value of 1 and 0 in the controller.
So is there anything that I am missing?
Or should I manually set the session by myself using the session() method?
Thank you.
If you want to manually authenticate users, you can easily use sessions. Have the following code as reference:
//this is where I would like to auth the user.
//using Auth::Attempt and Auth::Login will only run the default query
// typically you store it using the user ID, but you can modify the session using other values.
session()->put('user_id', user id from database here);
And if you want to check whether user is authenticated, modify RedirectIfAuthenticated middleware to this:
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (session()->has('user_id')) {
return redirect( custom path here );
}
return $next($request);
}
}
When you want to logout the user, simply destroy the session key
session()->forget('user_id');
**Note: ** many broadcasting and addons use Laravel's authentication system (Guards) and you may need to hook into their code if you want to use them with your custom auth system
Laravel provides Custom Session Drivers which you can use to create or delete your sessions
<?php
namespace App\Extensions;
class MongoSessionHandler implements \SessionHandlerInterface
{
public function open($savePath, $sessionName) {}
public function close() {}
public function read($sessionId) {}
public function write($sessionId, $data) {}
public function destroy($sessionId) {}
public function gc($lifetime) {}
}
Hope it helps, if not then comment down below. Will help you out.
###### Update #######
I think then you do have to make custom HTTP sessions from Laravel
Step 1: Create another table in your database for session, like this;
Schema::create('sessions', function ($table) {
$table->string('id')->unique();
$table->unsignedInteger('user_id')->nullable();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->text('payload');
$table->integer('last_activity');
});
Step 2: Store data in the session, you will typically use the put method or the session helper;
// Via a request instance...
$request->session()->put('key', 'value');
// Via the global helper...
session(['key' => 'value']);
Step 3: Get the key for specific user when your function returns 1
$value = $request->session()->get('key', function () {
return 'default';
});
Step 4: Delete the session, after some time you need to delete the session for security reasons then you can do.
$value = $request->session()->pull('key', 'default');
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
On my app, I'm trying to make it so that if a user has a certain condition, he will ALWAYS be redirected to a certain page, no matter which route he tries to access. In this case, it's if he doesn't have a username (long story).
ComposerServiceProvider.php :
public function boot() {
View::composer('templates.default', function ($view) {
if(Auth::user()) {
if (Auth::user()->username == null || Auth::user()->username == "") {
return redirect()->route('auth.chooseUsername');
}
So I figured the place to do this would be
ComposerServiceProvider.php.
However, I'm noticing that my redirect don't work in ComposerServiceProvider.php. And laravel.log doesn't give me an error or reason why.
The if condition is being met. If I replace return redirect()->route('auth.chooseUsername'); with dd('test');, sure enough all my pages return 'test'.
Why is this happening?
Try this steps:
You can use middleware for this scenario like below:
Create middleware php artisan make:middleware CheckPoint
Inside App\Http\Middleware\CheckPoint.php File
use Closure;
class CheckPoint
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if($request->user()) {
if ($request->user()->username == null || $request->user()->username == "") {
return redirect()->route('auth.chooseUsername');
}
}
return $next($request);
}
}
2. Add the middleware inside the app\Http\kernel.php
protected $routeMiddleware = [
'checkPoint' => \App\Http\Middleware\CheckPoint::class,
];
Then you can use it inside your route file and controller like below
Route::get(...)->middleware('checkPoint');
Route::middleware('checkPoint')->group(function() {
//Group of routes
.....
});
More About Middleware
controller middleware
In App\Http\Middleware create a new middleware:
<?php
namespace App\Http\Middleware;
use Closure;
class CheckYourCondition
{
public function handle($request, Closure $next)
{
if (! $request->user()->yourCondition()) {
return redirect('your_target_routre');
}
return $next($request);
}
}
Register your middleware by adding it to protected $routeMiddleware in App\Http\Kernel.
and assing it to the 'web' middleware group in protected $middlewareGroups.
For details see
The ComposerServiceProvider has a different purpose. It is used to register View Composers.
View composers are callbacks or class methods that are called when a
view is rendered. If you have data that you want to be bound to a view
each time that view is rendered, a view composer can help you organize
that logic into a single location.
See View Composers.
i'm writing an application in Laravel Spark 1.0 (Laravel 5.2). I wrote a custom middleware for agent (api) authentication. This is the code:
<?php
namespace App\Http\Middleware;
use App\Agent;
use Closure;
use Illuminate\Http\Request;
class AgentAuth
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if( isset($request->token) && !empty($request->token) )
{
$agent = Agent::where('token', '=', $request->token)->first();
if( $agent != NULL )
{
$team = $agent->Team()->first();
$user = $team->User()->first();
$request->merge(['team' => $team ]);
$request->merge(['user' => $user ]);
return $next($request);
}
else {
return response('Unauthorized 2.', 401);
}
}
else {
return response('Unauthorized 1.', 401);
}
}
}
In the default laravel authentication the user object is injected in the request (see laravel docs): https://laravel.com/docs/5.2/authentication#retrieving-the-authenticated-user
So you can retrieve the user using:
$request->user();
Spark obviously use this method to check if user subscription is valid (laravel\spark\src\Http\Middleware\VerifyUserIsSubscribed):
if ($this->subscribed($request->user(), $subscription, $plan, func_num_args() === 2)) {
return $next($request);
}
And it's not working because, with my middleware, you can retrieve the user using: $request->user; but not with the laravel defaults $request->user();
How should i inject the user object into the request?
Thank you in advance
EDIT:
Laravel in the service provider (Illuminate\Auth\AuthServiceProvider#registerRequestRebindHandler)
Use this code to bind object user to the request:
/**
* Register a resolver for the authenticated user.
*
* #return void
*/
protected function registerRequestRebindHandler()
{
$this->app->rebinding('request', function ($app, $request) {
$request->setUserResolver(function ($guard = null) use ($app) {
return call_user_func($app['auth']->userResolver(), $guard);
});
});
}
I tried to insert this code, with the appropriate correction, in the middleware but i can't figure out how to make it work.
I don't have a copy of Spark to try this & ensure what I'm doing is correct for you, but I think this will help:
1) An assumption - I believe you are saying that yes, this line will get you the user you want:
$user = $team->User()->first();
and you merely want to bind it to the request so that you can access this user later in your app via:
$request->user()
2) If this is true, then all I did was simplify the code you provided to add:
$request->merge(['user' => $user ]);
//add this
$request->setUserResolver(function () use ($user) {
return $user;
});
// if you dump() you can now see the $request has it
dump($request->user());
return $next($request);
I also $request->user() in the route closure, and it is there.
The app rebinding was a little strange to me, and didn't seem necessary. I'm not sure that anything would really need this for what you are doing.
You could use the auth system if that model implements the right interface, to log them in for the request.
Auth uses a rebinder to assign the userResolver on request. (So you get $request->user() from it). Check Illuminate\Auth\AuthServiceProvider#registerRequestRebindHandler to see how its setting that resolver.
$request->setUserResolver(....)
This is a very useful question. I was having trouble with the selected solution though. In my middleware I could successfully see $request->user(), however it was failing when using gates, namely in the Access/Gate class:
protected function raw($ability, $arguments = [])
{
if (! $user = $this->resolveUser()) {
return false;
}
// ...
This function is always returning false :/
So I did it as suggested here (http://laravel-recipes.com/recipes/230/setting-the-currently-authenticated-user), namely:
$usr = new User();
$usr->setAttribute('id', $request->user_id);
Auth::setUser($usr);
And it appears to be working without using setUserResolver().
Thanks
If you have the user ID you can easily authenticate the user with \Illuminate\Support\Facades\Auth::onceUsingId($user_id)
This updates the $request object. For example:
public function test(Request $request)
{
Auth::onceUsingId(19);
$next = new \App\Http\Controllers\OtherController();
return $next->otherMethod($request);
}
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.