Do I get a 422 HTTP code even though I am logged in? - laravel

Do I get a 422 HTTP code even though I am logged in?
From my blade I send an XHR post request. In the route I use the auth middleware. This works. This means you have to be logged in to send the post.
web.php
Route::post('/posts', [PostController::class, 'store'])->middleware(['web', 'auth'])->name('posts.store');
Now I created my own request class to validate the sent data.
PostStoreRequest authorise method
public function authorize()
{
return false;
}
Since I use my own custom request class I get this error message even though I am logged in:
This action is unauthorized.", exception: "Symfony\\Component\\\HttpKernel\\Exception\\AccessDeniedHttpException
I wonder why this is?

You have to check in the authorize() method if the user is authorised for this action. If you have a role system right you can implement this here. For example, only users with the Writer role are allowed to create a post. If you don't have that and you just allow everyone who is logged in, then change the return to true or return auth()->check().
Example without role system:
public function authorize()
{
return true;
// or
return auth()->check();
}
With role System:
public function authorize()
{
return auth()->user()?->isWriter();
}
Important Note: Thank to #matiaslauriti && #Tpojka for the right advice / good review.

Related

refresh page after updated model - Laravel

I want to refresh current page after any user update
I'm trying to do something like that in User Model :
public static function boot()
{
self::updated(function ($model) {
return back(); //or redirect(Request::url())
});
}
but it wasn't working.
How can I refresh the page if any user updated
In general, the model event functions creating/created/updating/updating/saved/saving should only ever return false or nothing (void). Returning false in for instance updating (that is: before the model is persisted in the database) cancels the model update (this works for all propagating events, see https://laravel.com/docs/9.x/events#stopping-the-propagation-of-an-event).
To "refresh" the current page after a user update, you have to be more specific about what you require. The back() function that you use (or the aliases redirect()->back() or even app('redirect')->back()) is (to my knowledge) to be used in controllers and it uses the Referer header or a property in the user's session to determine to which page to send the client to. This is most often used with validation errors, so that you can send the user back to the page they came from along with any possible validation error messages.
Using back() (or any other request completions like return response()->json('mydata')) inside events is wrong and it doesn't even work since the result of such events is not being used to complete the request. The only valid thing you "could" do is to try validation inside an event, which in turn could throw ValidationExceptions and is therefore automatically handled.
What you should do is use the controller method that actually handles the request that updates the user:
// UserController.php
/** (PUT) Update a user */
public function update(Request $request, User $user)
{
if($user->update($this->validate($request, [/* validations */])) {
return redirect()->back();
// or even be explicit and use `return redirect()->route('users.show', $user);`
}
// `update()` returned false, meaning one of the model events returned `false`
// (there is no exception thrown here).
session()->flash('alert-info', 'Something went wrong');
return redirect()->back();
}

Laravel Wrong Login Redirect

So I have the route lsapp.test/firms/1 which isn't available if you are not logged in.
But if I go to that route without being logged in is redirecting me to the login.After I log in it redirects me back to lsapp.test/firms/1, not the dashboard, and I get an error.
I've changed $redirectTo in the controllers to /dashboard but still isn't working.
If I go to /login and login it works.
How I can fix this?
public function __construct()
{
$this->middleware('auth');
}
public function show($id)
{
$employes = Firm::find($id)->employes()->paginate(5);
return view('firms.show')->with("employes", $employes);
}
Error: Call to a member function employes() on null.
The findOrFail methods will retrieve the first result of the query; however, if no result is found, a Illuminate\Database\Eloquent\ModelNotFoundException will be thrown.
If the exception is not caught, a 404 HTTP response is automatically sent back to the user. It is not necessary to write explicit checks to return 404 responses when using these methods.
public function show($id)
{
$employes = Firm::findOrFail($id)->employes()->paginate(5);
return view('firms.show')->with("employes", $employes);
}

How to hide login form after reaching the total of failed login attempts?

I want to hide the login form and display an error message instead, but I can't.
I tried to put the code below that rewrites the action on the controller that shows the form, but the method that checks for too many login attempts doesn't seem to work and never returns true.
public function showLoginForm(Request $request)
{
if (method_exists($this, 'hasTooManyLoginAttempts') &&
$this->hasTooManyLoginAttempts($request) ) {
$seconds = $this->limiter()->availableIn($this->throttleKey($request));
return view('auth.block', array(
'seconds' => $seconds
));
}
return view('auth.login');
}
I managed the authentication process with php artisan make: auth login controller is the default generated by Laravel, the only change is in the action that displays the form.
The function hasTooManyLoginAttempts() needs, in the $request, the username (usually the email) as a key to know if the user has reached his max login attempts.
If, in the $request, there is not the username with a value the function is unable to verify the user login attempts.
So you cannot really know who is the user that wants to get your login form, you know who is only after he submitted the form.
IMHO the only way could be to add a username parameter to the GET request but you shoud provide it with some workarounds: cookies, session etc.
Looking at Laravel's code, it checks for hasTooManyLoginAttempts based on throttleKey and maxAttempts.
The throttleKey is dependent on the user's email and IP address. So the output of the following code is something like: info#example.com|127.0.0.1 and that is your throttleKey.
protected function throttleKey(Request $request)
{
return Str::lower($request->input($this->username())).'|'.$request->ip();
}
Now Laravel gets the user's email (username) from $request->input($this->username()) when you send a POST request, which you don't have access to in the showLoginForm method because it's called on the GET request.
Anyway, if you want to block the login form you'll need to come up with your own unique throttleKey and then override the method. Say you want your throttleKey to be based only on the IP address - which is not recommended. Here's how you do it:
// In LoginController.php
protected function throttleKey(Request $request)
{
return $request->ip();
}

Laravel Policy with Spatie Permission check gives 403 for client credentials API request

I'm using Laravel Policy and checking for permissions created using Spatie's Laravel-Permissions package.
For an API call with client credentials, the authorizeResource() in the Controller constructor returns 403. If this is removed, it returns the expected results.
NpoPolicy.php
public function view(User $user, Npo $npo)
{
return $user->can('npo.view');
}
NpoController.php
public function __construct()
{
$this->authorizeResource(Npo::class);
}
api.php
Route::middleware('client')->resource('/npo', 'NpoController');
API Request
URL: https://my-app.dev/api/npo/1
Method: GET
When I comment out the authorizeResource method in the controller constructor, I get the result as expected:
{
"npos": {
"id":1,
"name":"Bailey and Sons",
"contact_person_name":"Mr. Davion Mayert",
"created_at":"2019-06-13 17:39:25",
"updated_at":"2019-06-13 17:39:25"
}
}
I'm aware that a Laravel policy requires a User model object and that is why the policy is returning 403 response in my case. Is there a general practice to handle API requests (with client credentials) in these cases?
You have missed the second parameter at authorizeResource function so, at the NpoController.php change the authorizeResource to:
$this->authorizeResource(Npo::class, 'npo');

CakePHP 2 AJAX redirections

I'm using AJAX in my web-app stuffs like search but if the user has been logged out, the ajax function return nothing because the redirection (from the action 'search' to the action 'login') has not been handled correctly.
Is it possible to redeclare the method 'redirect' in AppController to render the right action when a redirect hapend in an AJAX call ?
Thank you,
Sébastien
I think your best bet would be to setup you ajax to call to respond correctly to an invalid response. As it seems to be an important part of your app I would pass a 'loggedin' variable with every ajax request, so the client can tell as soon as the user has been logged out.
Update
In the case you want to keep a user logged in, you simply have to put the logged in/cookie check in something like your AppController::beforeFilter() that gets run with every request. for example:
public function beforeFilter() {
if($this->Auth->user() {
// USer is logged in, it's all gravy
} else {
// User is not logged in, try to log them in
$userData = $this->Cookie->read('User');
if(!empty($userData)) {
// Function that grabs info from cookie and logs in user
}
}
}
This way there will be no redirect as the user will be logged in as long as they have a cookie.
Another approach would be to allow everyone access to the Ajax function:
public function beforeFilter() {
$this->Auth->allow(array('my_ajax_method'));
}
And then check the user is authenticated in the method itself:
public function my_ajax_method() {
if (!$this->Auth->user()) {
//user not authenticated
$result = "requires auth";
}
else {
// use is authenticated
// do stuff
$result = 'result of stuff';
}
$this->set(compact('result'));
}
You will need to check the result of the ajax call in your javascript and act accordingly.

Resources