Laravel override authentication validation - php

I'm trying to override the custom Laravel authentication by checking if the user confirmed his/her email address. At the same time I would like to log authentication attempts.
The latter I'm trying to achieve with two listeners which I registered in EventServiceProvider.php
'Illuminate\Auth\Events\Login' => [
'App\Listeners\LogSuccessfulLogin',
],
'Illuminate\Auth\Events\Failed' => [
'App\Listeners\LogFailedAuthenticationAttempt',
],
In app/Http/Controllers/Auth/LoginController.php I have the following.
/**
* Validate the user login request.
* Override vendor method.
*
* #param \Illuminate\Http\Request $request
* #return void
*/
protected function validateLogin(Request $request)
{
$rules = [
'email' => [
'required',
'string',
'exists:users,email,confirmed,1',
],
'password' => [
'required',
'string',
],
];
$messages = [
'email.exists' => 'The combination of email address and password is unknown or the email address has not yet been validated.',
];
$validator = Validator::make($request->all(), $rules, $messages);
// $this->validate($request, $rules, $messages);
if ($validator->fails()) {
return redirect('nl')
->withErrors($validator)
->withInput();
}
}
The problem is that this way it seems to ignore to check if the user confirmed it's email address. And auth:middleware is used. Now change this:
//$validator = Validator::make($request->all(), $rules, $messages);
$this->validate($request, $rules, $messages);
Now it's checked if the address is confirmed, but the Failed event is never called. How can I achieve both calling the Event and the custom rules?

Related

Return error response if a body params is not mentionned in the validator

I'm using the Laravel Validator to test my incoming request. So to be sure that the request contains an username and an email, I write something like that:
$validator = Validator::make($request->all(), [
'username' => 'required',
'email' => 'required|email'
]);
if ($validator->fails()) {
return response()->json('error', 400);
};
But if in my request I have an additionnal params like name, the validator will not consider it as an error and will not fail.
Have you an idea how I can make my validator more strict so that the request body match exactly?
Technically it's not a validation fail in the Validator. But you could check if there are extra (unexpected) fields and send a JSON response based on that if you wanted.
Something like this maybe?
$validationRules = [
'username' => 'required',
'email' => 'required|email'
];
$validator = Validator::make($request->all(), $validationRules);
// Check if there are extra (unexpected) fields and fail in that case
$extraFields = $request->except(array_keys($validationRules));
if (count($extraFields) > 0) {
return response()->json('error because there are extra fields', 400);
}
if ($validator->fails()) {
return response()->json('error', 400);
}
return response()->json('ok', 200);
I hope it can help you.
Validation rules can be attached only to properties, so I don't think Laravel's Validator is able to do that. But you still can explicitly forbid certain fields by providing a custom closure rule to them, like this:
$denied = function($attr, $value, $fail) {
$fail("{$attr} is denied.");
};
$validator = Validator::make($request->all(), [
'username' => 'required',
'email' => 'required|email',
'name' => $denied,
'password' => $denied
]);
Otherwise you need to implement custom validation logic, probably utilizing the rule array from your validator as a white list.

"Method [validateEach] does not exist" Laravel 4.2

I've created custom validator:
namespace App\Validators;
class PhoneValidationRule extends \Illuminate\Validation\Validator {
public function validatePhone($attribute, $value, $parameters)
{
return preg_match("/^[\+]?[-()\s\d]{4,17}$/", $value);
}
}
and registered it:
class ValidatorServiceProvider extends ServiceProvider {
public function boot()
{
\Validator::resolver(function($translator, $data, $rules, $messages)
{
return new PhoneValidationRule($translator, $data, $rules, $messages);
});
}
...
and it works fine if i call it for field:
$validator = Validator::make($input, [
'emails' => 'required|each:email',
'phone' => 'required|phone',
]);
but when i try to apply it for array:
$validator = Validator::make($input, [
'emails' => 'required|each:email',
'phones' => 'required|each:phone',
]);
i get error message:
error: {type: "BadMethodCallException", message: "Method
[validateEach] does not exist.",…} file:
"/home/.../vendor/laravel/framework/src/Illuminate/Validation/Validator.php"
line: 2564 message: "Method [validateEach] does not exist." type:
"BadMethodCallException"
what i'm doing wrong?
Your problem is this part: required|each.
There is no such thing as a each validation rule. Take a look at the docs for a list of available validation rules: docs
Validating an individual field
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'phone' => 'required|phone',
]);
Validating arrays
$validator = Validator::make($request->all(), [
'emails' => 'required|array',
'emails.*' => 'email',
'phones' => 'required|array',
'phones.*' => 'phone',
]);
* Laravel 5.3+
each()
The problem was partially solved by straight calling native method $v->each() for custom rule phone:
$validator = Validator::make($input, [
'phones' => 'required|array',
]);
$validator->each('phones', ['required', 'phone']);
but it allows you to iterate validation only for arrays of values but not objects

dingo api validator rule need to fix

public function store(Request $request) {
$response = array('response' => '', 'success'=>false);
$rules = [
'email' => 'required|email',
'password' => 'required'
];
$validator = \Validator::make($request->all(), $rules);
if($validator->fails()){
$response['response'] = $validator->messages();
return $this->response->error($response, 401);
// or
return $this->response->error($validator, 401);
}else{
User::create($request->all());
}
}
How can I set validator in laravel using dingo API? I tried above code but does not work can't understand where is the right reference to keep track error logs
Please guide.
$rules = [
'username' => 'required',
'password' => 'required'
];
$payload = app('request')->only('username', 'password');
$validator = app('validator')->make($payload, $rules);
if ($validator->fails()) {
throw new Dingo\Api\Exception\StoreResourceFailedException('Invalid username provided.', $validator->errors());
}
You can try this
public function store()
{
$rules = [
'email' => 'required|email',
'password' => 'required'
];
$payload = app('request')->only('username', 'password');
$validator = app('validator')->make($payload, $rules);
if ($validator->fails()) {
throw new Dingo\Api\Exception\StoreResourceFailedException('Could not create new user.', $validator->errors());
}
User::create($request->all());
// send a success response
}
This example is taken from the documentation of Dingo and customized based on your code.
The best way I've found to do validation especially when using Dingo API is to use Form Requests.
When using Dingo API however, you use
use Dingo\Api\Http\FormRequest;
instead of
use App\Http\Requests\Request;
like in normal form requests.
So in your case, you'd have a form request like
<?php
namespace App\Http\Requests;
use Dingo\Api\Http\FormRequest;
class CreateUser 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 [
'email' => 'required|email',
'password' => 'required'
];
}
}
So this keeps validations outside your controller. And your controller function can just be
public function store(Request $request) {
User::create($request->all());
}
If you are not very familiar with Form Requests, this is a great chance to look at it. Cheers.

Custom Laravel validation messages

I'm trying to create customized messages for validation in Laravel 5. Here is what I have tried so far:
$messages = [
'required' => 'Harap bagian :attribute di isi.',
'unique' => ':attribute sudah digunakan',
];
$validator = Validator::make($request->all(), [
'username' => array('required','unique:Userlogin,username'),
'password' => 'required',
'email' => array('required','unique:Userlogin,email'),$messages
]);
if ($validator->fails()) {
return redirect('/')
->withErrors($validator) // send back all errors to the login form
->withInput();
} else {
return redirect('/')
->with('status', 'Kami sudah mengirimkan email, silahkan di konfirmasi');
}
But it's not working. The message is still the same as the default one. How can I fix this, so that I can use my custom messages?
Laravel 5.7.*
Also You can try something like this. For me is the easiest way to make custom messages in methods when you want to validate requests:
public function store()
{
request()->validate([
'file' => 'required',
'type' => 'required'
],
[
'file.required' => 'You have to choose the file!',
'type.required' => 'You have to choose type of the file!'
]);
}
If you use $this->validate() simplest one, then you should write code something like this..
$rules = [
'name' => 'required',
'email' => 'required|email',
'message' => 'required|max:250',
];
$customMessages = [
'required' => 'The :attribute field is required.'
];
$this->validate($request, $rules, $customMessages);
You can provide custom message like :
$rules = array(
'URL' => 'required|url'
);
$messages = array(
'URL.required' => 'URL is required.'
);
$validator = Validator::make( $request->all(), $rules, $messages );
if ( $validator->fails() )
{
return [
'success' => 0,
'message' => $validator->errors()->first()
];
}
or
The way you have tried, you missed Validator::replacer(), to replace the :variable
Validator::replacer('custom_validation_rule', function($message, $attribute, $rule, $parameters){
return str_replace(':foo', $parameters[0], $message);
});
You can read more from here and replacer from here
For Laravel 8.x, 7.x, 6.x
With the custom rule defined, you might use it in your controller validation like so :
$validatedData = $request->validate([
'f_name' => 'required|min:8',
'l_name' => 'required',
],
[
'f_name.required'=> 'Your First Name is Required', // custom message
'f_name.min'=> 'First Name Should be Minimum of 8 Character', // custom message
'l_name.required'=> 'Your Last Name is Required' // custom message
]
);
For localization you can use :
['f_name.required'=> trans('user.your first name is required'],
Hope this helps...
$rules = [
'username' => 'required,unique:Userlogin,username',
'password' => 'required',
'email' => 'required,unique:Userlogin,email'
];
$messages = [
'required' => 'The :attribute field is required.',
'unique' => ':attribute is already used'
];
$request->validate($rules,$messages);
//only if validation success code below will be executed
//Here is the shortest way of doing it.
$request->validate([
'username' => 'required|unique:Userlogin,username',
'password' => 'required',
'email' => 'required|unique:Userlogin,email'
],
[
'required' => 'The :attribute field is required.',
'unique' => ':attribute is already used'
]);
//The code below will be executed only if validation is correct.
run below command to create a custom rule on Laravel
ı assuming that name is CustomRule
php artisan make:rule CustomRule
and as a result, the command was created such as PHP code
if required keyword hasn't on Rules,That rule will not work
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class CustomRule implements Rule
{
/**
* Create a new rule instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
//return true or false
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The validation error message.';
}
}
and came time using that
first, we should create a request class if we have not
php artisan make:request CustomRequest
CustomRequest.php
<?php
namespace App\Http\Requests\Payment;
use App\Rules\CustomRule;
use Illuminate\Foundation\Http\FormRequest;
class CustomRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules(): array
{
return [
'custom' => ['required', new CustomRule()],
];
}
/**
* #return array|string[]
*/
public function messages(): array
{
return [
'custom.required' => ':attribute can not be empty.',
];
}
}
and on your controller, you should inject custom requests to the controller
your controller method
class FooController
{
public function bar(CustomRequest $request)
{
}
}
You can also use the methods setAttributeNames() and setCustomMessages(),
like this:
$validation = Validator::make($this->input, static::$rules);
$attributeNames = array(
'email' => 'E-mail',
'password' => 'Password'
);
$messages = [
'email.exists' => 'No user was found with this e-mail address'
];
$validation->setAttributeNames($attributeNames);
$validation->setCustomMessages($messages);
For those who didn't get this issue resolve (tested on Laravel 8.x):
$validated = Validator::make($request->all(),[
'code' => 'required|numeric'
],
[
'code.required'=> 'Code is Required', // custom message
'code.numeric'=> 'Code must be Number', // custom message
]
);
//Check the validation
if ($validated->fails())
{
return $validated->errors();
}
$rules = [
'name' => 'required',
'email' => 'required|email',
'message' => 'required|max:250',
];
$customMessages = [
'required' => 'The :attribute field is required.',
'max' => 'The :attribute field is may not be greater than :max.'
];
$this->validate($request, $rules, $customMessages);
In the case you are using Request as a separate file:
public function rules()
{
return [
'preparation_method' => 'required|string',
];
}
public function messages()
{
return [
'preparation_method.required' => 'Description is required',
];
}
Tested out in Laravel 6+
you can customise the message for different scenarios based on the request.
Just return a different message with a conditional.
<?php
namespace App\Rules;
use App\Helpers\QueryBuilderHelper;
use App\Models\Product;
use Illuminate\Contracts\Validation\Rule;
class ProductIsUnique implements Rule
{
private array $attributes;
private bool $hasAttributes;
/**
* Create a new rule instance.
*
* #return void
*/
public function __construct(array $attributes)
{
$this->attributes = $attributes;
$this->hasAttributes = true;
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
$brandAttributeOptions = collect($this->attributes['relationships']['brand-attribute-options']['data'])->pluck('id');
$query = Product::query();
$query->when($brandAttributeOptions->isEmpty(), function ($query) use ($value) {
$query->where('name', $value);
$this->hasAttributes = false;
});
return !$query->exists();
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return ($this->hasAttributes) ? 'The Selected attributes & Product Name are not unique' : 'Product Name is not unique';
}
}
Laravel 10.x
If you are using Form Requests, add another method called messages(): array in your request.
class YourRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => 'required',
'email' => 'required|email',
...
];
}
//Add the following method
public function messages(): array
{
return [
'email.required' => 'Custom message for Email Required',
];
}
}
Then the message will be displayed automatically once the request is send from the form.

Custom validation rules

I want to create a validation to a form that resets the password.
I have a method that checks whether the new password are the same and have more than 6 characters.
I miss only one thing to check if given the previous password is consistent.
/**
* Reset user password
*
* #return void
*/
public function reset_password(Request $request){
$this->validate($request, [
'old_password' => 'required',
'password' => 'required|min:6|confirmed'
]);
$user = Auth::user();
$user->password = bcrypt($request->password);
$user->save();
return redirect('/user/profile');
}
Try this:
\Hash::check($request->password, $user->password);
The best way to do this is to create new validation rule: Custom Validation Rules.
What I did (not tested):
// Somewhere inside your app service provider boot()
Validation::extend('notSamePassword', function($attribute, $value, $parameters, $validator) {
return !Hash::check($attribute, $parameters[0]);
}
And then in your controller/place where you validate:
$password = 'password hash to validate with';
$this->validate($request, [
'old_password' => 'required',
'password' => 'required|min:6|confirmed|notSamePassword:'.$password
]);

Categories