How can I validate the request user in Laravel? - laravel

I am sending a update request like:
Route::put('user/{user}/edit-user-education', 'UpdateUserEducationController#editUserEducation');
My controller is :
class UpdateUserEducationController extends Controller
{
public function editUserEducation(UserEducation $education, User $user, EditUserEducationRequest $request)
{
$education->school = $request->school;
$education->degree = $request->degree;
$education->user_id = $user->id; // here to validate
$education->save();
return response()->json([
'message' => 'Education Updated'
]);
}
}
Now how I can validate the request user_id with the user_id already in inserted in DB ? I want to ensure that the only user can update the record who created that one.
How to do so ? Thanks in advance

Check out the docs on validation here:
https://laravel.com/docs/8.x/validation
Specifically, I think you want the exists rule:
https://laravel.com/docs/8.x/validation#rule-exists
The quick and dirty way is to add your validation in the controller but there are some better methods as explained in the docs. I usually opt for Form Requests, which it looks like you've already done as your request is an instance of EditUserEducationRequest.
In the controller you can add:
$validated = $EditUserEducationRequest->validate([
'user_id' => 'required|exists:users',
]);
I assume your user table is called users.
You could alternatively state the exists validation rule for user_id in the rules array of your Form Request as per the docs.
EDIT:
I actually missed a requirement in your original post that is that the user sending the request must be the same user as the one being updated.
That can be handled in the the authorize method of your form request with something like:
public function authorize()
{
return $this->user()->id == $this->user_id;
}

Simply make a check that current user is the same user who is trying to update record.
class UpdateUserEducationController extends Controller
{
public function editUserEducation(UserEducation $education, User $user, EditUserEducationRequest $request)
{
if($user->id==Auth::user()->id){
$education->school = $request->school;
$education->degree = $request->degree;
$education->user_id = $user->id; // here to validate
$education->save();
return response()->json([
'message' => 'Education Updated'
]);
}
else{
return response()->json([
'error' => 'Invalid User'
]);
}
}
}

Related

Laravel: How can I return more data from a custom UserProvider?

I have successfully created a custom user provider and have implemented a user interface.
I am trying to save the user object to my local Postgres database that is returned from a third party backend.
Here is what my retrieveByCredentials method looks like:
// MyCustomUserProvider.php
public function retrieveByCredentials(array $credentials) {
if (empty($credentials)) {
return null;
}
try {
$response = Http::asForm()
->retry(3, 100)
->timeout(5)
->post('https://my-custom-auth-endpoint', $credentials)
->json();
if ($response) {
$user = User::firstOrNew([
'foo' => $response['foo'],
]);
$user->save();
return $user;
}
} catch (RequestException $e) {
Log::info('-----------------------------------------------------------');
Log::info(print_r($e->getMessage(), true));
Log::info('-----------------------------------------------------------');
return $e->getMessage();
}
}
...
public function validateCredentials(Authenticatable $user, array $credentials): bool {
return strtoupper($user->foo) === strtoupper($credentials['foo']);
}
So far, everything is working great. I am getting the correct values/booleans for each required method. However, I need to be able to return additional info to my controller than just a boolean value. For example, if a user's password has expired, this information is coming from my 3rd party database. So far, it looks like returning the user results in a true or false response in my LoginController. If I get false back from my CustomProvider I can set a flash message of, "Invalid Credentials". But that may not always be the appropriate error.
Here is how I am calling the CustomUserProvider:
// LoginController.php
$authenticated = Auth::guard('foo')
->attempt([
'userid' => $request->username,
'password' => $request->password,
]);
// $authenticated is the response from CustomUserProvider.
dd($authenticated) // true or false.
It is understood that in my provider I just want to authenticate the user. How can I return the authenticated user back to the controller? Additionally, how can I return the error back to the controller so I can display a proper error message?
The function Auth::guard('foo')->attempt([]) will also return a boolean to tell user if the authentication is successful or not. If you want to get the auth user, just return Auth::user() after the calling that function, like return response()->json(Auth::user())

I build a user invitation system with Laravel it does not work

I am building a user invitation system with Laravel that does not work I have the error here email invitation after sending the email to the user the descriptive image is lower Click here to activate!
my InviteController
public function process(Request $request)
{
// validate the incoming request data
do {
//generate a random string using Laravel's str_random helper
$token = str_random();
} //check if the token already exists and if it does, try again
while (Invite::where('token', $token)->first());
//create a new invite record
$invite = Invite::create([
'email' => $request->get('email'),
'token' => $token
]);
// send the email
Mail::to($request->get('email'))->send(new InviteCreated($invite));
// redirect back where we came from
return redirect()
->back();
}
public function accept($token)
{
// Look up the invite
if (!$invite = Invite::where('token', $token)->first()) {
//if the invite doesn't exist do something more graceful than this
abort(404);
}
// create the user with the details from the invite
User::create(['email' => $invite->email]);
// delete the invite so it can't be used again
$invite->delete();
// here you would probably log the user in and show them the dashboard, but we'll just prove it worked
return 'Good job! Invite accepted!';
}
my image error
Do a dd of $invite before passing to the mail to make sure you have the records:
//create a new invite record
$invite = Invite::create([
'email' => $request->get('email'),
'token' => $token
]);
dd($invite);
// send the email
Mail::to($request->get('email'))->send(new InviteCreated($invite));
Then on InviteCreated make sure you store $invite on a public property so its available on the blade template:
class InviteCreated extends Mailable{
public $invite;
public function __construct($invite){
$this->invite = $invite;
}
...... if you use markdown then pass the `$invite`..
public function build()
{
return $this
->markdown(your markdown mail')
->with([
'invite'=>$this->invite
]);
}
}

Laravel 5.5 - Show specific category from API response

I am returning an API response inside a Categories controller in Laravel 5.5 like this...
public function get(Request $request) {
$categories = Category::all();
return Response::json(array(
'error' => false,
'categories_data' => $categories,
));
}
Now I am trying to also have the option to return a specific category, how can I do this as I am already using the get request in this controller?
Do I need to create a new route or can I modify this one to return a specific category only if an ID is supplied, if not then it returns all?
Better case is to create a new route, but you can also change the current one to retrieve all models if the parameter is not supplied. You first gotta choose which approach you will be using. For splitting it into multiple calls you can see Resource controllers and for using one method you can follow Optional Route Parameters
It will be much cleaner if you will create another route. For example
/categories --> That you have
/categories/{id} -> this you need to create
And then add method at same controller
public function show($id) {
$categories = Category::find($id);
return Response::json(array(
'error' => false,
'categories_data' => $categories,
));
}
But if you still want to do it at one route you can try something like this:
/categories -> will list all categories
/categories?id=2 -> will give you category of ID 2
Try this:
public function get(Request $request) {
$id = $request->get('id');
$categories = $id ? Category::find($id) : Category::all();
return Response::json(array(
'error' => false,
'categories_data' => $categories,
));
}

How to add expiry date condition to login function in laravel 5.2

In laravel 5.2, i want to add the condition so that only users where their expiry date is greater than today's date to login.
protected function getCredentials(Request $request)
{
return ['email' => $request->{$this->loginUsername()}, 'password' => $request->password];
}
The code does not accept adding:
'expires' => gte(Carbon::now())
Any help is appreciated
I don't think this is possible, even in Laravel 5.5. Taking a look at the retrieveByCredentials method in Illuminate\Auth\EloquentUserProvider which is used to get the user from the database, you can see that the query passes simple key/value combinations to the where method on the $query object, which equate to where key = value. This is from 5.5:
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials) ||
(count($credentials) === 1 &&
array_key_exists('password', $credentials))) {
return;
}
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a user, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
$query = $this->createModel()->newQuery();
foreach ($credentials as $key => $value) {
if (! Str::contains($key, 'password')) {
$query->where($key, $value);
}
}
return $query->first();
}
To achieve what you are after I would recommend doing this check after the user has logged in, in your controller for instance:
// Imagine this is the controller method where you're dealing with user logins
public function login(array $credentials)
{
if (! auth()->attempt($credentials)) {
// Handle what happens if the users credentials are incorrect.
}
$user = auth()->user();
if (Carbon::now()->gte($user->expires)) {
// User's account has expired, lets log them out.
auth()->logout();
// Return a redirect with a message or something...
}
// Handle a successful login.
}
I'm not sure if the auth() helper is available in 5.2, but you should be able to use the Auth facade to do the same thing, e.g. Auth::attempt(...).

How can I use Laravel Validation in the API, it's returning my view now?

I have this route in route/api.php:
Route::post('/register', 'LoginController#register');
My LoginController
class LoginController extends Controller {
public function register(Request $request) {
$this->validate($request, // <-- using this will return the view
// from **web.php** instead of the expected json response.
[
'email' => 'required|email',
'firstName' => 'required|alpha_dash',
'lastName' => 'required',
'password' => 'required|confirmed',
]);
$input = $request->all();
//$plain_password = $input['password'];
$input['uuid'] = uuid();
$input['password'] = Hash::make($input['password']);
$user = User::create($input);
dd($errors);
$response['succes'] = true;
$response['user'] = $user;
return response($response);
}
}
Why does adding a validation call change the behaviour to returning my view / the wrong route. I want the api to validate my request too, not just my "frontend".
When you use Laravel's validate method from controller it automatically handles/takes the step if the validation fails. So, depending on the required content type/request type, it determines whether to redirect back or to a given url or sending a json response. Ultimately, something like thos following happens when your validation fails:
protected function buildFailedValidationResponse(Request $request, array $errors)
{
if ($request->expectsJson()) {
return new JsonResponse($errors, 422);
}
return redirect()->to($this->getRedirectUrl())
->withInput($request->input())
->withErrors($errors, $this->errorBag());
}
So, if the first if statement is true then you'll get a json response and it'll be true if you either send an ajax request or if you attach a accept header with your request to accept json response (When requesting from a remote server). So, make sure your request fulfills the requirements.
Alternatively, you can manually validate the request using the Validator component and return a json response explicitly if it fails.

Resources