laravel using validation request with except method - laravel

I am using request validation as
php artisan make:request ClientRequest
As you can see, on client edit form if password field is not empty I am able to use $request->validated() method on database update,
However if password field empty(user dont want to change password),
I am not able to use $request->except('password')->validated() method.
I use $request->except() method due to this situation.
Does this pose a security problem?
public function update(ClientRequest $request, Client $client)
{
$validated = $request->validated();
if($request->filled('password') )
{
Client::whereId($client->id)->update($validated);
}else{
Client::whereId($client->id)->update($request->except('password'));
}
return redirect('/clients')->with('success', 'success');
}

Client::whereId($client->id)->update($request->except('password'));
That line is does pose a big security problem especially if you are relying on validation to set fields rather than the fillable attribute. $request->except('password') will return all the other fields that the user submitted so if the user had added something like is_admin => true in the request, you'll end up setting it on the db if it exists.
You can use \Illuminate\Support\Arr::except() on the validated data to make sure that you are only getting the data you expect. That would change the that particular line to
Client::whereId($client->id)->update(\Illuminate\Support\Arr::except($request->validated(), 'password'));
PS: You already have the client through route model binding so you don't need to query it you can update that client directly i.e
$client->update(\Illuminate\Support\Arr::except($request->validated(), 'password'));

You are validating all fields sent to update() in both scenarios.
You would have had an issue if you sent the password field in both cases, but only validated it in one of them. That's not the case.
So looks fine to me from that perspective.

Related

Make email authentication case insensitive in Laravel 5.7

I use the default email authentication in Laravel 5.7.3 (just updated from 5.4) with a sqlite DB. At login my email address gets validated case sensitive which is not what I want. It should be allowed to login with "JohnDoe#foobar.com" as well as "johndoe#foobar.com".
I've tried to add an accessor at my user model to always lowercase the database value.
public function getEmailAttribute($value) {
return strtolower($value);
}
But this one doesn't seem to be used by Auth at all. Additionally I don't know where to change the user input in the incomming request to lower case.
Is there a simple config case sensitive switch? Or where do I need to change/add scripts?
Thanks for your support
Daniel
Your accessor should be fine, but you should make sure that you also lowercase the given value, e.g. In your controller:
Assuming that you're using the default LoginController shipped from Laravel:
I overwrote the credentials method from AuthenticatesUsers in App\Http\Controllers\Auth\LoginController
protected function credentials(Request $request)
{
$credentials = [
$this->username() => strtolower($request->get($this->username())),
"password" => $request->get("password")
];
return $credentials;
}
This works fine, when all emails stored in the database are already stored all-lowercase. If you're starting from scratch you can enforce the emails to be stored lowercase by default. Just implement the following mutator in your App\User:
public function setEmailAttribute($value)
{
$this->attributes['email'] = strtolower($value);
}
If you have stored mixed-case email already, you can update all emails to lowercase emails using this query:
UPDATE users SET email = LOWER(email);
But this still feels kind of incomplete and you maybe don't want to manipulate your data this way. I am pretty much sure that there are more possibilities to make this happen but unfortunately I am not able to dig into this further for now. However my next attempt would be to extend/write a custom UserProvider.
You have to call getEmailAttribute(/your email here/)
before login and signup like this
$request->email = getEmailAttribute($request->get('email'));

How to properly forward parameters from a POST result and avoid refresh (re-submit), with Laravel 5?

This is a basic issue, when submitting and performing a post, you normally want to avoid giving the user the chance to re-iterate the submit by refreshing or going back and forth with browser history.
(Backend re-submit, controls and checking is not questioned here. Of course they should be in place for security)
My solution was to have in Laravel web routes defined a post and a get route.
Like:
Route::post('/action', 'ActionController#doAction')->name('do.action');
Route::get('/action', 'ActionController#doActionConfirm')->name('do.action.confirm');
In ActionController something like:
public function doAction(Request $request)
{
....
return redirect()->route('do.action.confirm', [ 'data' =>
base64_encode(json_encode([
'action_id'=> $action_id,
'sent_to'=> $email,
'sent_success'=> $sent_success,
'sent_errors'=> $sent_errors,
]))]);
}
....
public function doActionConfirm(Request $request)
{
return view('confirmation')->with('data',$request->input('data'));
}
Then in my confirmation.blade.php get that $data decoded and displayed.
Is this the correct way to do it with Laravel?
What I don't like here:
is there's a better and more elegant way to forward data from the redirect()
is there a better way to manage get parameters to the GET route just to avoid people understanding and trying to inject unwanted data to the confirmation blade? (even if they will obtain nothing but no-sense views..because the page is doing nothing, it's simply displaying data in HTML blade)

How to add additional data to object Auth in Laravel 5.3?

There is default object Auth in Laravel after authification.
It contents data about current user from table Users.
How can I add the additional data to this object from other related table?
Edit:
So, if I am right, the object Auth is created when user is authenticated. In this moment I need to fill object by additional data.
I presume you want to retrieve a user in a controller and return it as a response, maybe json? or not, it's doesn't really matter. here what you could do
public function getUser()
{
$user = auth()->user();
$user->load('relationName');
$user->load('anotherRelationName');
}

How to "Refresh" the User object in Laravel?

In Laravel you can do this:
$user = Auth::user();
Problem is, if I do changes on items on that object, it will give me what was there before my changes. How do I refresh the object to get the latest values? I.e. To force it to get the latest values from the DB?
You can update the cache object like this.
Auth::setUser($user);
for Example
$user = User::find(Auth::user()->id);
$user->name = 'New Name';
$user->save();
Auth::setUser($user);
log::error(Auth::user()->name)); // Will be 'NEW Name'
[This answer is more appropriate for newer versions of Laravel (namely Laravel 5)]
On the first call of Auth::user(), it will fetch the results from the database and store it in a variable.
But on subsequent calls it will fetch the results from the variable.
This is seen from the following code in the framemwork:
public function user()
{
...
// If we've already retrieved the user for the current request we can just
// return it back immediately. We do not want to fetch the user data on
// every call to this method because that would be tremendously slow.
if (! is_null($this->user)) {
return $this->user;
}
...
}
Now if we make changes on the model, the changes will automatically be reflected on the object. It will NOT contain the old values. Therefore there is usually no need to re-fetch the data from the database.
However, there are certain rare circumstances where re-fetching the data from the database would be useful (e.g. making sure the database applies it's default values, or if changes have been made to the model by another request). To do this run the fresh() method like so:
Auth::user()->fresh()
Laravel does do that for you, HOWEVER, you will not see that update reflected in Auth::user() during that same request. From /Illuminate/Auth/Guard.php (located just above the code that Antonio mentions in his answer):
// If we have already retrieved the user for the current request we can just
// return it back immediately. We do not want to pull the user data every
// request into the method because that would tremendously slow an app.
if ( ! is_null($this->user))
{
return $this->user;
}
So if you were trying to change the users name from 'Old Name' to 'New Name':
$user = User::find(Auth::user()->id);
$user->name = 'New Name';
$user->save();
And later in the same request you try getting the name by checking Auth::user()->name, its going to give you 'Old Name'
log::error(Auth::user()->name)); // Will be 'Old Name'
A little late to the party, but this worked for me:
Auth::user()->update(array('name' => 'NewName'));
Laravel already does that for you. Every time you do Auth::user(), Laravel does
// First we will try to load the user using the identifier in the session if
// one exists. Otherwise we will check for a "remember me" cookie in this
// request, and if one exists, attempt to retrieve the user using that.
$user = null;
if ( ! is_null($id))
{
$user = $this->provider->retrieveByID($id);
}
It nulls the current user and if it is logged, retrieve it again using the logged id stored in the session.
If it's not working as it should, you have something else in your code, which we are not seeing here, caching that user for you.

Laravel 4: How to use auth.php to fit two models?

I knew the Auth::attempt method is correspond the app/config/auth.php which specifies which model should be used.
I have a problem,how can I do if I have two model that want to be validated?
For example ,I have a Administrator table,And also a User's table.
Can somebody help if you can understand what I'm saying?
Thanks...
You can make any complex login by using the Auth::loginUsingId method, and using the query builder. For instance:
if (/*Any complex validation result*/) {
Auth::loginUsingId( /*the id you want*/ );
//.....
Session::put('some_var',$some_value);
//.....
return Redirect::to('/some_path');
} else {
return Redirect::back()
->withInput()
->with('message', 'Error');
}
Also you need to set in your file app/config/auth.php the driver value to database instead of eloquent by default.

Resources