Laravel keeps passing GenericUser to Policies instead of my User Model - php

I am trying to use the standard Laravel auth related functionality. I have the standard login, register, logout, etc... working fine. But my problem is that whenever I go to use functions like Auth::user() this is returning the GenericUser model rather than my own App/Models/User.php model. The problem is that my User's model has some foreign key references to other data so I would like to work exclusively with my User model.
For example, when trying to make a Policy I am running into an error:
Argument 1 passed to App\Policies\ContractPolicy::before() must be an
instance of App\Models\User, instance of Illuminate\Auth\GenericUser
given
And the code simply looks like this:
<?php
namespace App\Policies;
use App\Models\Contract;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class ContractPolicy
{
use HandlesAuthorization;
public function before(User $user, $ability)
{
if ($user->isSuperAdmin()) {
return true;
}
}
public function viewAny(User $user)
{
return $user->isMember();
}
}
The GenericUser model is indeed returning the data from the users table, but the problem is that it is completely different from what my User model gives, and thus includes nothing like the foreign key references or functions I added. I was under the impression that I could switch the model used by switching the config/auth.php provider. Which I did and nothing seemed to change:
'providers' => [
'users' => [
'model' => App\Models\User::class,
],
'users' => [
'driver' => 'database',
'table' => 'users',
],
],
What am I doing wrong here?

You are defining the users provider to use the 'database' driver:
'users' => [
'driver' => 'database',
'table' => 'users',
],
Adjust that to use the 'eloquent' driver instead:
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
The 'database' driver uses Query Builder to interact with the database table and returns an object of GenericUser to represent the currently authenticated user.
The 'eloquent' driver uses an Eloquent Model to interact with the database and returns a Model instance. If you like relationships and all the other features of Eloquent then you should stick with using the 'eloquent' driver. Also, you have defined your Policy methods to be expecting a particular Model passed to its methods.

Related

How to change 'users' auth table and use another instead Laravel

So I just started a Laravel Project with Breeze, and I wanted to change the default table users , the problem is it didn't work, I did my research for days and I didn't get any successful result
I will try to explain what is the problem and what have I tried so far.
First, I created a new table called users_data, and this table, is completely different than the users table.
The fields that users_data has, are for example: name_value, password_value, age_value, email_value, etc. (I have to mention too that for the table users_data, it doesn't use a migration, because I already have an sql file, and added it directly to the db (I already have tables created, with primary keys, and foreign key, so i couldn't do the migration because it would take me a lot of time), and without the migration I can still get the data, so I don't think it could be this the problem).
Actually I'am using Breeze, however, I used Auth scaffolding (PHP artisan make: Auth) too
What have I tried:
After several days of search, first I have created a new Model, called UsersModel, the content of this is the same as User Model however what I change is:
protected $table = 'users_data';
protected $fillable = [
*name_value*,
*password_value*,
];
and an extra function to override the default password of breeze or Auth (I guess):
public function getAuthPassword()
{
return $this->password_value;
}
next I went to conf/auth.php
there I specified the Model:
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\UsersModel::class,
],
and the table to use:
/*
|--------------------------------------------------------------------------
| Authentication Table
|--------------------------------------------------------------------------
|
| When using the "Database" authentication driver, we need to know which
| table should be used to retrieve your users. We have chosen a basic
| default value but you may easily change it to any table you like.
|
*/
'table' => 'users_data',
after this I went to the view login.blade.php, and changed only the email input (for what I read on different pages, changing the password input will cause different problems, because I would need to make a lot of changes to make it work so the best idea is to override it with getAuthPassword, specified in the model):
new name input:
x-input id="email" class="block mt-1 w-full" type="text" name="name_value" :value="old('name_value')" required autofocus />
After all this I went to LoginRequest (the validation for the login), where I replaced email for name_value
I tried to debug this:
dd(Auth::attempt($this->only('name_value', 'password'), $this->boolean('remember')));
and returns false
I noticed that there's a function in vendor/laravel/ui/auth-back/AuthenticatesUsers
called username(), that returns 'email'
when I saw that I remembered a page that said that this function could override too, so I changed the return value to name_value, and it doesn't do nothing
last, just to clarify,
I don't need the Register site I only need the login page, so for that in the $fillable I didn't add all the columns of the database, just the ones that I need to log in (name_value, password_value)
If anyone could help me and guide me it will be great, because I'am running out of ideas (I could do it with PHP alone, however, I need the ->middleware ['Auth], is there a way to activate the middlware if the user exists?)
So you might have a model named users_data.php. Go inside it and change the code to something like that.
STEP:1
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\users_data as Authenticatable; //Add this line for Auth.
class users_data extends Authenticatable //Extends child from Authenticatable parent class obj.
{
use HasFactory;
protected $fillable = ['column1', 'column2', 'column3', .....];
}
STEP:2
Go to config/auth.php. You may found something like this below.
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
Change the 'model' => App\Models\User::class, to 'model' => App\Models\users_data::class,
This is the main character in this drama that putting your application to users table by default.
STEP:3
Comment off the User.php so that no future conflict create.
Now your application has been diverted to your desired table and ready to login.
I don't think it's good practice to be editing vendor files. We don't push them to version control so other developers won't have your changes. Also, Laravel already has a way to override the username value without editing the vendor files.
Just use the trait in your auth controller like this:
public class MyLoginController {
use AuthenticatesUsers; // or you can also use ThrottlesLogins trait
// then override the username function here
public function username() {
return 'name_value';
}
}
To override the password you can define this on your User model:
public function getPasswordAttribute() {
return $this->attributes['password_value'];
}
public function getAuthPassword() {
return $this->password_value;
}
I haven't tested this but based on the docs this is how you should do it. Also make sure to read this Laravel doc.

Laravel / Lumen ReadOnly Model?

There are some tables in our system which are being fed using 3rd party APIs and our system is supposed only read data from them and never Insert or Update anything.
Is there any feature in Laravel/Lumen, where we can mention in the Model to disallow/disable insert/update queries like we have the option public $timestamps = false; to disable the timestamps?
The tables are in same database else we would have restricted based on MySQL user.
There are a few ways.
OPTION 1: Probably the quickest is this "read-only" model trait. https://github.com/michaelachrisco/ReadOnlyTraitLaravel
It protects you from...
create
forceCreate
save
update
firstOrCreate
firstOrNew
delete
destroy
restore
forceDelete
performDeleteOnModel
push
finishSave
performUpdate
touch
insert
truncate
OPTION 2: A completely different way to do it is on the db config and model connection. So, this has 2 parts.
project/config/database.php
Duplicate and tweak the db connection.
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
...
'readonly' => [
'driver' => 'mysql',
'read' => [
'host' => env('DB_HOST', '127.0.0.1')
],
'write' => [
'host' => 'this.will.404'
],
...
project/app/MyReadOnlyModel.php
class MyReadOnlyModel extends Model
{
protected $connection = 'readonly';
...
}
If you are caught in the trap of wanting the Model to be writable sometimes... I would suggest having 2 models on the same table.
app/Normal/MyModel.php
app/ReadOnly/MyModel.php
yes,
as a secure way:
as you can restricting some actions on Database.
ReadOnly Model
but you can disable the eloquent models too.
laravel models are extended from Illuminate\Database\Eloquent\Model
you can extend an ReadOnlyModel from Model.
then extend any model you want from that class.
this class should Override any method which writes data in db,
so i follow the source code:
Update and updateOrFail, push and etc was using used Model->save() method.
While create,creteOrFail , delete and etc were places in Builder which uses Model->save() method
The save() method used performUpdate or performInsert with someevent triggering ...
so the simplest thing you can do to stop model from touching databases is to implement:
<?php
namespace App\ReadOnlyDB;
use Illuminate\Database\Eloquent\Model;
/**
* Just Extend all of Your Models from This Class
*
*/
class ReadOnlyModel extends Model
{
public function save(){
// do nothing
}
}
save() methods in Relation and BelongsTo classes would use Model->save() method too.
The most secure way will be to create a second MySQL user with the readonly only on the tables.
Then in Laravel you can create a specific connection with the restricted MySQL user in your config/database.php.
Then in the Model specify the wanted connection through the protected $connection property.
I think the only proper way to manage this is to have access restrictions at the MySQL user side. Here's why:
If you are stubbing (disabling) save/update methods from the Eloquent class, you only disable save/updates for your own code. This is analogous to locking a door while hanging the key on the door handle. Nothing prevents you from doing the saves/updates since you are not bound to use the Eloquent class.
In addition, 3rd party packages may persist data which does not depend on your code. You basically have to check every single line of code of theirs to make sure your database is not updated.
Therefore, apply these restrictions at the database user level. Then, when for some reason your (or anyone elses) code calls $myModel->save(), you should face an exception from the ORM that states you do not have permission. Then, handle it accordingly (like returning a particular HTTP status code).
Create an additional mySql user with read-only privileges.
in .env file add the following
DB_CONNECTION_SECOND=mysql
DB_HOST_SECOND=127.0.0.1
DB_PORT_SECOND=3306
DB_DATABASE_SECOND=database
DB_USERNAME_SECOND=user_2
DB_PASSWORD_SECOND=secret
in config/database.php add the following
'mysql2' => [
'driver' => env('DB_CONNECTION_SECOND'),
'host' => env('DB_HOST_SECOND'),
'port' => env('DB_PORT_SECOND'),
'database' => env('DB_DATABASE_SECOND'),
'username' => env('DB_USERNAME_SECOND'),
'password' => env('DB_PASSWORD_SECOND'),],
in your controller specify the connection name..
DB::connection('mysql2')->select(...);
Maybe using an empty fillable attribute in your model resolve your problem!
protected $fillable = [];
Set a model accessor to throw an exception when touching an attribute.
But this is more of a read-only attribute instead of a read-only model since it requires one accessor per attribute.
use Exception;
use Illuminate\Database\Eloquent\Casts\Attribute;
protected function value(): Attribute
{
return Attribute::make(
set: fn () => throw new Exception('Model is readonly'),
);
}

Set Default Guard After Register/Login When Using Multiple Guards in Laravel

I am working on a Laravel API project where the user's table has a column of type with a value of either student or company.
For the purpose of authenticating the users of both types and restricting access to certain routes, I have set up two guards with the same driver (JWT) and provider (Users).
'company' => [
'driver' => 'jwt',
'provider' => 'users',
'hash' => false,
],
'student' => [
'driver' => 'jwt',
'provider' => 'users',
'hash' => false,
],
In the login and register method of my controller, I am accessing the value of user type from the form where the user selects his role (student or company).
public function register(){
$user= $this->create(request()->all());
$token=auth(request('type'))->login($user);
return $this->respondWithToken($token);
}
The purpose to access the user type is to pass that value to the auth() method which in turn uses it to log in the user through that specific guard.
The issue is: I have to use that request(type) in every controller method wherever authentication is necessary and there is need to use use the auth() related methods. e.g jwt respondwithtoken() method
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth(request('type'))->factory()->getTTL() * 60
]);
}
What I want: I want a mechanism to set the guard dynamically after the user has successfully logged in and I should able be to use the auth() method without passing any argument to it.
// shoulduseguarddynamically(request('type'));
P.S: Please also verify that using guards in this way when I have single table with multi-type users is the right approach?
First off, your approach is not really safe - just tampering with the 'type' will grant you access to places you are not supposed to go.
You would be better using Gates instead of guards for this,
You can check them in the docs here: https://laravel.com/docs/7.x/authorization#gates

how to specify more than one authenticated guard in laravel

i'm reading in Authentication section in laravel website https://laravel.com/docs/5.2/authentication
can anyone explain how I can do this , like the documentation explains , to specify separate tables for authentication ... i will quotes from laravel like below :
Accessing Specific Guard Instances
You may specify which guard instance you would like to utilize using
the guard method on the Auth facade. This allows you to manage
authentication for separate parts of your application using entirely
separate authenticatable models or user tables.
The guard name passed to the guard method should correspond to one of
the guards configured in your auth.php configuration file:
if (Auth::guard('admin')->attempt($credentials)) {
//
}
You kinda have to read the examples of adding custom guards and providers, the configuration part of it mainly. You can use the same auth 'driver', you just want to adjust what model is used by the Auth user provider.
config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
// add another one
// use the same driver, 'session', but a different user provider
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
// add a provider using Eloquent but using a different model
'admins' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
],
]
Then you should be able to specify the guard admin to Auth. As long as that Admin model implements Authenticatable and you are passing the appropriate credentials to attempt on Auth you should be good.
If you have more middlewares and you want to apply more than one guard and by this i mean middleware you can do this in any Middleware.php file:
public function handle($request, Closure $next)
{
// if a user is Agency or Admin, let him through
if (Auth::user()->isAgency() || Auth::user()->isAdmin()) {
return $next($request);
}
// else show error page
abort(403);
}

How to use laravel's Auth class in different table?

I created a simple login and registration in my page and I also added some new columns in the default users table. Now my problem is I have another table named as admin and the default Laravel's Auth table is users. How can I implement the same functionality in my table?
In the Users model it has the declaration for the table name
protected $table = 'users';
Can you give me an example how can I use the default laravel's auth class?
Thats all thanks. :-)
Laravel takes default users table for an application. For a change of laravel authentication different table relevant table name, we need to make a small change in authentication file of config.
Go to
config/auth.php
'providers' => [
// 'users' => [
// 'driver' => 'eloquent',
// 'model' => App\User::class,
// ],
'users' => [
'driver' => 'database',
'table' => 'user',
],
],
Do you hear about Multiauth in laravel. in this library there are two or more type user can login in one laravel application. In our case there are two type user Admin and Public that means User right.
Both forgot password and reset password functionality works separately in one application.
After install this library have have one step like below.
'multi' => [ 'admin' => [ 'driver' => 'database', 'table' => 'admin', 'email' => 'client.emails.password' ], 'users' => [ 'driver' => 'database', 'table' => 'users', 'email' => 'client.emails.password', ] ],
change your Auth.php file code with this one.
installation
Firstly you want to include this package in your composer.json file.
"require": {
"sboo/multiauth" : "4.0.*"
}
Now you'll want to update or install via composer.
composer update
Usage
Everything is done the exact same way as the original library, the one exception being that all method calls are prefixed with the key (account or user in the above examples) as a method itself.
Auth::admin()->attempt(array(
'email' => $attributes['email'],
'password' => $attributes['password'],
));
Auth::client()->attempt(array(
'email' => $attributes['email'],
'password' => $attributes['password'],
));
Auth::admin()->check();
Auth::client()->check();
Here is your library
I don't think the best way is to duplicate your table. I would extend users table with a role field that indicates if the user is a standard one or an admin. This way, you can keep the same code and add the ADMIN functionality that you are looking for.
If you NEED to do that and you are using Laravel 4, maybe you can use this plugin:
https://github.com/ollieread/multiauth/
Also in this thread you have code that implements Auth in different tables:
https://gist.github.com/danielcoimbra/64b779b4d9e522bc3373
But I strongly suggest to integrate both tables in one with an Admin flag/field

Categories