How to create a custom password field for Laravel Auth - php

I need to login users registered in the table Igrejas that have the fields responsavel_cpf and responsavel_senha, but Laravel in the function validateCredentials expects the name 'password'.
public function validateCredentials(UserContract $user, array $credentials)
{
$plain = $credentials['password'];
}
I tried to login using attempt without success:
$userdata = array(
'responsavel_cpf' => Input::get('email'),
'responsavel_senha' => Input::get('password')
);
if (Auth::guard('igrejas')->attempt($userdata)) {
return Redirect::to('dashboard_paroquia');
} else {
return Redirect::to('login');
}
What do I do to replace the default fields email and password with responsavel_cpf and responsavel_senha?

You can override the password column in your user model like so:
// User.php
public function getAuthPassword()
{
return $this->custom_pw_field;
}
However, if you actually want to pass an array that does not explicitly contain password to Auth::guard('xxx')->attempt($credentials) (for which there is no reason!), you'll probably have to override and add the Illuminate\Auth\EloquentUserProvider manually which seems a lot of work.
So I would suggest to just use the following:
Auth::guard('xxx')->attempt([
'email' => $request->post('email'),
'password' => $request->post('password')
]);
The password key should then validate against the custom_pw_field that you defined.
Explanation
By looking at the source of Illuminate\Auth\EloquentUserProvider and checking the function public function retrieveByCredentials(array $credentials), you can see what it does:
Find the first record in the auth table that matches all the conditions in the $credentials array except for password (so just email in the example above). So you could add for instance another key like is_webmaster like $authGuard->attempt(['email' => $request->post('email'), 'is_webmaster' => 1]) which would then retrieve the first user record that has these properties.
After this record is retrieved, the hash from its password column is then checked against your input.

Related

Laravel 8: Checking user's data with database table does not work properly

I have created a two factor authentication system, and it redirects user to token.blade.php where he must enters the token that is going to be sent to his phone number and also stored at active_codes table.
Now I want to check if the user has entered the same token code that was stored at active_codes table which looks like this:
And then at the Controller, I tried this:
public function submit(Request $request)
{
$data = $request->validate([
'token' => 'required|min:6'
]);
if($data['token'] === auth()->user()->activeCode()->code){
dd('same');
}
}
But when I enter the same token, I get this error:
ErrorException Undefined property:
Illuminate\Database\Eloquent\Relations\HasMany::$code
so my question is how to check if the requested token code of user, is as same as the token code which is stored on the DB ?
Note: The relationship between User and ActiveCode Models is OneToMany.
User.php:
public function activeCode()
{
return $this->hasMany(ActiveCode::class);
}
ActiveCode.php:
public function user()
{
return $this->belongsTo(User::class);
}
Your solution is pretty easy, you are not doing ->first() or ->get(), so you are trying to access a model property on a HasMany class.
This code should be similar to:
auth()->user()->activeCode()->first()->code
But if you have more than 1 code, then you should do something like:
public function submit(Request $request)
{
$data = $request->validate([
'token' => 'required|min:6'
]);
if(auth()->user()->activeCode()->where('code', $data['token'])->exists()){
dd('same');
}
}

Laravel Validation Request, how to handle validation on update?

First of all I love the way that validation is going through, can now easily use
public function authorize(Authenticator $auth)
{
return $auth->user()->hasRole('administrator');
}
hat's not the problem, I bump always into another problem... that is when you update an record, how to do things with the rules? If I need to update an email, I need the following string: 'email' => 'unique:users,email_address,10'. In this case it should look like:
public function rules()
{
return [
'email' => 'required|unique:users,id,?????',
'tags' => 'required'
];
}
It's more simple.
The Laravel documentation says "If your table uses a primary key column name other than id, you may specify it as the fourth parameter":
'email' => 'unique:users,email_address,'.$user->id.',user_id'
If for example, you want to verify if a username exists, but excluding current user ID:
// UpdateUserRequest.php
public function rules() {
//
return [
'username' => 'required|unique:users,username,' . $this->id . ',id',
];
}

PHP Laravel Auth password from users table, id from another table

I've been looking around the internet for a while now and I'm not able to find an answer to my question which is why I am asking it here.
I want to authenticate users to login but this must be done with information from two separate tables.
I am sending to values to my controller: badge and password
I am accessing them with
$request->badge (note: This is the account id)
$request->password (note: this is the users password)
Previously I have tried the following:
public function store(Request $request)
{
$request->validate([
'badge' => 'required|int',
'password' => 'required|string',
]);
$account = Account::select('id', 'user_id')->where('id', $request->badge)->first();
if(!$account)
return back()->withInput()->withErrors(['password' => 'Incorrect badge or password!']);
else
{
if(Auth::attempt(['username' => $accounts->user->username, 'password' => $request->password]))
{
return redirect()->route('home');
}
else
{
return back()->withInput()->withErrors(['password' => 'Incorrect badge or password!']);
}
}
}
This will log the user in, however when I use Auth::id() it returns the ID of the user and not of the account.
example: $request->badge is filled with 25 (which is the account id), the user id is 1. Auth::id returns 1 instead of my desired 25.
My table looks like the following:
users
----
id
username
email
password
accounts
-----
id
user_id
name
I have a relationship in accounts and users to link them together
public function accounts()
{
return $this->hasMany(Accounts::class, 'user_id', 'id');
}
public function user()
{
return $this->belongsTo(User::class, 'id', 'user_id');
}
I want auth::id to give me 25 instead of 1.
Since you already have the ID if the correct account it's only a hasOne relationship between the account and the related user.
In your auth config, you need to change the model of the users provider to your actual account model:
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\Account::class,
],
],
In the account model we're then adding a global scope which always fetches the password from the related user entry so auth won't have a problem with it:
class Account extends Model
{
protected $table = 'accounts';
protected static function boot()
{
parent::boot();
static::addGlobalScope('withPassword', function ($builder) {
$builder
->join('users', 'accounts.user_id', 'users.id')
->select(DB::raw('accounts.*, users.password'));
});
}
}
The scope makes sure that the password column is always present if you query an account. You can change it up so that the account always includes certain other columns as well, but for now, this should work as expected.
In chat, we've been discussing the topic of the advantages of manual authentication with either login or loginById in this scenario. A possible solution would be this:
$account = Account::select(
'accounts.id',
DB::raw('(SELECT `password` FROM users WHERE accounts.user_id = users.id) AS `password`'))
)
->where('accounts.id', $request->badge)
->first();
if ($account) {
if (Hash::check($request->password, $account->password)) {
Auth::loginUsingId($account->id);
return redirect()->route('home');
} else {
return back()->withInput()->withErrors(['password' => 'Incorrect badge or password!']);
}
} else {
return back()->withInput()->withErrors(['password' => 'Incorrect badge or password!']);
}
I might be on the totally wrong track here, but what you could do is to apply a global scope to the Users model which automatically joins the accounts table every time you query for a user.
This join automatically replaces the user_id with the account ID given how joins work but you may have to fiddle around with a raw select to get the values how you want them.
class User extends Model
{
/**
* The "booting" method of the model.
*
* #return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope('account', function (Builder $builder) {
$builder->join('accounts', 'users.id', ''accounts.user_id');
});
}
}
To remove the scope from any query you can just use User::withoutGlobalScope('account').
Read more about usage of global scopes here.

Laravel Validations where one filed is unique where other filed must have some id

I want to pass $params['user_id'] to $fieldValidations and check if the hour is unique for specific user_id not for all hours hour in the database table
I created a model post
class Post extends Model
{
protected $fillable = ['user_id', 'hour'];
public static $fieldValidations = [
'user_id' => 'required',
'hour' => 'required|date_format:Y-m-d H:i:s|unique:post,hour,NULL,user_id,'
];
}
and a controller post
class PostController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$params = $request->all();
$params['user_id'] = 12;
$validator = Validator::make($params, Post::$fieldValidations);
if ($validator->fails()) {
return Response::json($validator->errors()->all(), 422);
}
}
}
I don't think you can do this using the unique validation rule. From Laravel 5.7 documentation:
The field under validation must be unique in a given database table.
Note it says table and not column.
You may have to just query the database and return a JSON response error if it fails. Also, in your current code inside the validation rules, you are specifying that user_id is the primary id key column in the post table. I think that is likely an error and should be removed, even though it's irrelevant given that you can't accomplish what you want using the unique rule. Also, you ended the rule with a comma.
if (Post::where(['user_id' => $params['user_id'], 'hour' => $params['hour']])->exists()) {
return response()->json(['status' => 'error', 'msg' => 'Error', 'errors' => ['hour_error' => ['That hour already exists on the user!']]], 422);
}
Lastly, instead of using $params = $request->all(), I prefer to use the request() helper function and just inline it into the rest of the code. But, that's up to you.

Laravel validation service with unique fields

I'm using a validation service to validate user submitted form input (something along the lines of: http://laravel.io/bin/vrk).
Using this approach (validation service classes) to validate user submitted form data against a set of rules, how can I validate user submitted data when rules have a unique rule. For example, if a user has the username of John then when I try to update the model validation fails (because John exists as a username, even though it belongs to the current model).
To solve this in Laravel I can do something like 'username' => 'required|alpha_dash|unique:users,username'.$id. How should I modify my current code, in the link, to best accommodate this? Should I have separate validator classes depending on the scenario (for example, UserCreateValidator, UserUpdateValidator, etc). Or should I do something like create separate validation rules in UserValidator class and pass which rule I want as an argument to either the constructor or the passes() method when calling UserValidator?
I think you could do something like this
First update UserValidator rules like this.
class UserValidator extends Validator {
// Override parent class $rules
protected $rules = [
'default' => [
'username' => 'required|alpha_dash|unique:users',
'password' => 'required|between:6,16|confirmed',
'password_confirmation' => 'required|between:6,16'
],
'update' => [
'username' => null,
]
];
}
Then modify Validator's passes method like this
public function passes($rule = null) {
$rules = $this->rules['default'];
if ($rule && isset($this->rules[$rule])) {
$rules = array_merge($rules, $this->rules[$rule]);
}
$validator = \Validator::make($input, $rules);
if ($validator->fails()) {
$this->validator = $validator;
return false;
}
return true;
}
Then in your controller's PUT method, this will merge update rules to default rules
$rule = 'update';
// user has changed his username
if ($input['username'] !== $old_username) {
$rule = 'create'; // validate uniqueness
}
else {
unset($input['username']); // remove it, we don't validate it anymore since it's the same
}
$validator->passes($rule); // override 'default' rules with 'update' rules
You don't have to change your controller's POST method, it'll stay the same
$validator->passes(); // use 'default' rules
If I'm understanding right, you have issues updateng data because of primary key constraints on your model. What you need to do is to create 2 sets of rules, one for insert, and one for update.
Asuming you have a set of rules like this:
protected $rules = [
'id' => 'required|unique:users'
]
You should implement something like this:
protected $rules = [
'id' => 'required|unique|unique:users,id,' . $this->id
];
This should tell laravel to ignore the duplicate id in the table users for the specified id, in this case, the id for the current object.
You can read more about this on laravel's documentation at http://laravel.com/docs/validation
unique:table,column,except,idColumn
The field under validation must be unique on a given database table.
If the column option is not specified, the field name will be used.
Well, what are you doing on post?
Because this is what you should be doing:
$user = User::find($userId);
$user->username = $input['username'];
$user->email = $input['email'];
$user->save();
To update a record.
Or
$input = array('username' => 'w0rldart', 'email' => 'hahafu#dumbledore.com');
// Retrieve the user by the attributes, or create it if it doesn't exist,
// based on the data above, which can come from an Input::all();
$user = User::firstOrCreate($input);
... many possibilities. But you could also do:
$input = array_forget($input, 'username');
To comply with your case, by removing the username index from the input array.
This is all I call tell you, based on the information you gave us. If you want more, post the controller's put method.
Update:
Here's my version of your PUT method: http://laravel.io/bin/OaX
I really think that try catch syntax is useless, since it's obvious that a User model will always be there. But I still don't know what you're trying to update. Even though I can't test it right now, I don't think that updating should be giving that problem, and if it does, retrieve user by username/id then unset the username index in your input array, and update it according to your specifications.
A little modification in UserValidator class
class UserValidator extends Validator {
// Override parent class $rules
protected $rules = [
'username' => 'required|alpha_dash|unique:users',
'password' => 'required|between:6,16|confirmed',
'password_confirmation' => 'required|between:6,16'
];
// ADD THIS
public function __construct(Array $rules = array())
{
parent::__construct();
if(count($rules)){
foreach($rules as $k => $v) $this->rules[$k] = $v;
}
}
}
In your controller putUpdate method
$user = User::whereUsername($username)->firstOrFail();
$rules = ['username' => 'required|alpha_dash|unique:users,username,'. $user->id];
// Pass the rule to update the rule for username in this method
$validator = \Services\Validators\UserValidator(Input::all(), $rules);
Check the manual here.

Categories